<!DOCTYPE html>
<html lang=zh>
<head>
  <meta charset="utf-8">
  
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
  <meta name="renderer" content="webkit">
  <meta http-equiv="Cache-Control" content="no-transform" />
  <meta http-equiv="Cache-Control" content="no-siteapp" />
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="format-detection" content="telephone=no,email=no,adress=no">
  <!-- Color theme for statusbar -->
  <meta name="theme-color" content="#000000" />
  <!-- 强制页面在当前窗口以独立页面显示,防止别人在框架里调用页面 -->
  <meta http-equiv="window-target" content="_top" />
  <!-- 谷歌收录 -->
  <meta name="google-site-verification" content="-5hl8eC4wfapGZhZouwMVUcKG0iaynijVRJbqVlzXm4" />
  
  
  <title>MongoDB聚合示例 | 鴻塵</title>
  <meta name="description" content="文章说明文章作者：鴻塵文章链接：https:&#x2F;&#x2F;hwame.top&#x2F;20210814&#x2F;mongodb-aggregation-examples.html参考资料：  聚合示例来源：Practical MongoDB Aggregations（by Paul Done@TheDonester） Ebook源码：Github @pkdone MongoDB在线运行：MongoDB Web Shell">
<meta property="og:type" content="article">
<meta property="og:title" content="MongoDB聚合示例">
<meta property="og:url" content="https://hwame.top/20210814/mongodb-aggregation-examples.html">
<meta property="og:site_name" content="鴻塵">
<meta property="og:description" content="文章说明文章作者：鴻塵文章链接：https:&#x2F;&#x2F;hwame.top&#x2F;20210814&#x2F;mongodb-aggregation-examples.html参考资料：  聚合示例来源：Practical MongoDB Aggregations（by Paul Done@TheDonester） Ebook源码：Github @pkdone MongoDB在线运行：MongoDB Web Shell">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-1TopN查询.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-2分组汇总.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-3解包分组.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-4列表去重.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-5一对一联结.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-6多字段联结及一对多.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-7强类型转换.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-8残缺日期转换.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-9特征分类之分面检索.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-1最大图网络.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-2增量分析.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210818模拟漏单情况.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-3严格视图1.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-3严格视图2.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-4隐藏敏感字段.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-5IOT电力消耗1.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-5IOT电力消耗2.png">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210822梯形法则示例.png">
<meta property="article:published_time" content="2021-08-14T15:44:47.000Z">
<meta property="article:modified_time" content="2021-09-30T16:40:26.000Z">
<meta property="article:author" content="鴻塵">
<meta property="article:tag" content="MongoDB">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-1TopN查询.png">
  <!-- Canonical links -->
  <link rel="canonical" href="https://hwame.top/20210814/mongodb-aggregation-examples.html">
  
    <link rel="alternate" href="true" title="鴻塵" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png" type="image/x-icon">
  
  
<link rel="stylesheet" href="/css/style.css">

  
  
  
    <link href="//cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css" rel="stylesheet">
  
  
<meta name="generator" content="Hexo 5.4.2"></head>


<script src="https://cdn.jsdelivr.net/gh/bobcn/hexo_resize_image.js@master/hexo_resize_image.js"></script>
<body class="main-center theme-purple" itemscope itemtype="http://schema.org/WebPage">
  <header class="header" itemscope itemtype="http://schema.org/WPHeader">
  <div class="slimContent">
    <div class="navbar-header">
      
      
      <div class="profile-block text-center">
        <a id="avatar" href="https://hwame.top" target="_blank">
          <img class="img-circle img-rotate" src="https://cdn.jsdelivr.net/gh/hwame/pics@main/avatar.jpg" width="200" height="200">
        </a>
        <h2 id="name" class="hidden-xs hidden-sm">鴻塵</h2>
        <h3 id="title" class="hidden-xs hidden-sm hidden-md">Pythoner, Data Analyst</h3>
        <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i> 湖北-武汉</small>
      </div>
      
      <div class="search" id="search-form-wrap">

    <form class="search-form sidebar-form">
        <div class="input-group">
            <input type="text" class="search-form-input form-control" placeholder="搜索" />
            <span class="input-group-btn">
                <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i class="icon icon-search"></i></button>
            </span>
        </div>
    </form>
    <div class="ins-search">
  <div class="ins-search-mask"></div>
  <div class="ins-search-container">
    <div class="ins-input-wrapper">
      <input type="text" class="ins-search-input" placeholder="想要查找什么..." x-webkit-speech />
      <button type="button" class="close ins-close ins-selectable" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
    </div>
    <div class="ins-section-wrapper">
      <div class="ins-section-container"></div>
    </div>
  </div>
</div>


</div>
      <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
    </div>
    <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
      <ul class="nav navbar-nav main-nav menu-highlight">
        
        
        <li class="menu-item menu-item-home">
          <a href="/.">
            
            <i class="icon icon-home-fill"></i>
            
            <span class="menu-title">首页</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-archives">
          <a href="/archives">
            
            <i class="icon icon-archives-fill"></i>
            
            <span class="menu-title">归档</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-categories">
          <a href="/categories">
            
            <i class="icon icon-folder-open"></i>
            
            <span class="menu-title">分类</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-tags">
          <a href="/tags">
            
            <i class="icon icon-tags"></i>
            
            <span class="menu-title">标签</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-repository">
          <a href="/repository">
            
            <i class="icon icon-project"></i>
            
            <span class="menu-title">资源</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-gallery">
          <a href="/gallery">
            
            <i class="icon icon-delicious"></i>
            
            <span class="menu-title">相册</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-links">
          <a href="/links">
            
            <i class="icon icon-friendship"></i>
            
            <span class="menu-title">友链</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-about">
          <a href="/about">
            
            <i class="icon icon-cup-fill"></i>
            
            <span class="menu-title">关于</span>
          </a>
        </li>
        
      </ul>
      
	
    <ul class="social-links">
    	
        <li><a href="https://github.com/hwame" target="_blank" title="Github" data-toggle=tooltip data-placement=top><i class="icon icon-github"></i></a></li>
        
        <li><a href="https://weibo.com/hwamei" target="_blank" title="Weibo" data-toggle=tooltip data-placement=top><i class="icon icon-weibo"></i></a></li>
        
        <li><a href="https://www.zhihu.com/people/hwame" target="_blank" title="Zhihu" data-toggle=tooltip data-placement=top><i class="icon icon-zhihu"></i></a></li>
        
        <li><a href="https://segmentfault.com/u/hwame" target="_blank" title="Segmentfault" data-toggle=tooltip data-placement=top><i class="icon icon-segmentfault"></i></a></li>
        
        <li><a href="https://gitee.com/hwame" target="_blank" title="Gitee" data-toggle=tooltip data-placement=top><i class="icon icon-gitee"></i></a></li>
        
        <li><a href="/atom.xml" target="_blank" title="Rss" data-toggle=tooltip data-placement=top><i class="icon icon-rss"></i></a></li>
        
    </ul>

    </nav>
  </div>
</header>

  
    <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    
      <div class="widget">
    <h3 class="widget-title"><i style="color:#9400D3" class="icon icon-stackexchange"></i>公告</h3>
    <div class="widget-body">
        <div id="board">
            <div class="content">
                <p>欢迎来到鴻塵的博客!<br>鴻塵的<u><a target="_blank" rel="noopener" href="https://weibo.com/hwamei" style="color:#E541E5;">微博</a></u>主页，鴻塵的<u><a target="_blank" rel="noopener" href="https://github.com/hwame" style="color:#E541E5;">Github</a></u>主页，如果我可以忘记的<u><a target="_blank" rel="noopener" href="https://www.zhihu.com/people/hwame" style="color:#E541E5;">知乎</a></u>主页。</p>
            </div>
        </div>
    </div>
</div>

    
      
  <div class="widget">
    <h3 class="widget-title"><i style="color:#9400D3" class="icon icon-tags"></i>标签云</h3>
    <div class="widget-body tagcloud">
      <a href="/tags/CentOS/" style="font-size: 13px; color: #fff">CentOS</a> <a href="/tags/Go/" style="font-size: 13.2px; color: #fff">Go</a> <a href="/tags/Hexo/" style="font-size: 13.8px; color: #fff">Hexo</a> <a href="/tags/Linux/" style="font-size: 14px; color: #fff">Linux</a> <a href="/tags/Linux%E9%83%A8%E7%BD%B2/" style="font-size: 13px; color: #fff">Linux部署</a> <a href="/tags/MongoDB/" style="font-size: 13.4px; color: #fff">MongoDB</a> <a href="/tags/Spark/" style="font-size: 13px; color: #fff">Spark</a> <a href="/tags/matplotlib/" style="font-size: 13.2px; color: #fff">matplotlib</a> <a href="/tags/miscellanea/" style="font-size: 13.6px; color: #fff">miscellanea</a> <a href="/tags/python/" style="font-size: 14px; color: #fff">python</a> <a href="/tags/shell/" style="font-size: 13.6px; color: #fff">shell</a> <a href="/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/" style="font-size: 13px; color: #fff">大数据</a> <a href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/" style="font-size: 13px; color: #fff">数据结构与算法</a> <a href="/tags/%E7%88%AC%E8%99%AB/" style="font-size: 13px; color: #fff">爬虫</a>
    </div>
  </div>

<script type="text/javascript">
    var everytag=document.getElementsByClassName("widget-body tagcloud")[0].children;
    for (var i = everytag.length - 1; i >= 0; i--) {
    	var r=Math.floor(Math.random()*255);
        var g=Math.floor(Math.random()*255);
        var b=Math.floor(Math.random()*255);
        everytag[i].style.background = "rgb("+r+","+g+","+b+")";
    }
</script>
    
      
  <div class="widget">
    <h3 class="widget-title"><i style="color:#9400D3" class="icon icon-folder-open"></i>分类</h3>
    <div class="widget-body">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/Go/">Go</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Hexo/">Hexo</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Linux/">Linux</a><span class="category-list-count">8</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/MongoDB/">MongoDB</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/miscellanea/">miscellanea</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/python/">python</a><span class="category-list-count">7</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/">数据结构与算法</a><span class="category-list-count">1</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title"><i style="color:#9400D3" class="icon icon-archives-fill"></i>归档</h3>
    <div class="widget-body">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2022/">2022</a><span class="archive-list-count">4</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/">2021</a><span class="archive-list-count">9</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/">2020</a><span class="archive-list-count">17</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title"><i style="color:#9400D3" class="icon icon-shu-fill"></i>最新文章</h3>
    <div class="widget-body">
      <ul class="recent-post-list list-unstyled ">
        
          <li>
            
            <div class="item-thumb">
              <a href="/20220327/channel-and-goroutine-in-go.html" class="thumb">
    
    
        <span style="background-image:url(https://cdn.jsdelivr.net/gh/hwame/pics@main/post-pics/icon-golang.jpeg)" alt="Go语言中的通道" class="thumb-image"></span>
    
</a>

            </div>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/categories/Go/">Go</a>
              </p>
              <p class="item-title">
                <a href="/20220327/channel-and-goroutine-in-go.html" class="title">Go语言中的通道</a>
              </p>
              <p class="item-date">
                <time datetime="2022-03-27T11:06:16.000Z" itemprop="datePublished">2022-03-27</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-thumb">
              <a href="/20220323/when-to-use-pointer-in-go.html" class="thumb">
    
    
        <span style="background-image:url(https://cdn.jsdelivr.net/gh/hwame/pics@main/post-pics/icon-golang.jpeg)" alt="Go语言什么时候使用指针" class="thumb-image"></span>
    
</a>

            </div>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/categories/Go/">Go</a>
              </p>
              <p class="item-title">
                <a href="/20220323/when-to-use-pointer-in-go.html" class="title">Go语言什么时候使用指针</a>
              </p>
              <p class="item-date">
                <time datetime="2022-03-23T12:49:57.000Z" itemprop="datePublished">2022-03-23</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-thumb">
              <a href="/20220228/awesome-images-in-markdown.html" class="thumb">
    
    
        <span style="background-image:url(https://cdn.jsdelivr.net/gh/hwame/pics@main/avatar.jpg)" alt="Markdown中图片的高级用法" class="thumb-image"></span>
    
</a>

            </div>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/categories/miscellanea/">miscellanea</a>
              </p>
              <p class="item-title">
                <a href="/20220228/awesome-images-in-markdown.html" class="title">Markdown中图片的高级用法</a>
              </p>
              <p class="item-date">
                <time datetime="2022-02-28T15:27:50.000Z" itemprop="datePublished">2022-02-28</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-thumb">
              <a href="/20220116/shell-regular-expression.html" class="thumb">
    
    
        <span style="background-image:url(https://cdn.jsdelivr.net/gh/hwame/pics@main/post-pics/linux-icon.png)" alt="Shell正则表达式" class="thumb-image"></span>
    
</a>

            </div>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/categories/Linux/">Linux</a>
              </p>
              <p class="item-title">
                <a href="/20220116/shell-regular-expression.html" class="title">Shell正则表达式</a>
              </p>
              <p class="item-date">
                <time datetime="2022-01-16T14:13:05.000Z" itemprop="datePublished">2022-01-16</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-thumb">
              <a href="/20211009/install-linux-on-android-phone.html" class="thumb">
    
    
        <span style="background-image:url(https://cdn.jsdelivr.net/gh/hwame/pics@main/avatar.jpg)" alt="如何在安卓手机上安装Linux发行版" class="thumb-image"></span>
    
</a>

            </div>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/categories/miscellanea/">miscellanea</a>
              </p>
              <p class="item-title">
                <a href="/20211009/install-linux-on-android-phone.html" class="title">如何在安卓手机上安装Linux发行版</a>
              </p>
              <p class="item-date">
                <time datetime="2021-10-09T15:16:37.000Z" itemprop="datePublished">2021-10-09</time>
              </p>
            </div>
          </li>
          
      </ul>
    </div>
  </div>
  

    
  </div>
</aside>

  
  
<aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    <nav id="toc" class="article-toc">
      <h3 class="toc-title">文章目录</h3>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-%E5%9F%BA%E7%A1%80"><span class="toc-number">1.</span> <span class="toc-text">1.基础</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-TopN%E6%9F%A5%E8%AF%A2"><span class="toc-number">1.1.</span> <span class="toc-text">(1)TopN查询</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E5%88%86%E7%BB%84%E6%B1%87%E6%80%BB"><span class="toc-number">1.2.</span> <span class="toc-text">(2)分组汇总</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E8%A7%A3%E5%8C%85%E6%95%B0%E7%BB%84%E5%8F%8A%E5%88%86%E7%BB%84"><span class="toc-number">1.3.</span> <span class="toc-text">(3)解包数组及分组</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E5%88%97%E8%A1%A8%E5%8E%BB%E9%87%8D"><span class="toc-number">1.4.</span> <span class="toc-text">(4)列表去重</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-%E6%95%B0%E6%8D%AE%E8%81%94%E7%BB%93"><span class="toc-number">2.</span> <span class="toc-text">2.数据联结</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E4%B8%80%E5%AF%B9%E4%B8%80%E8%81%94%E7%BB%93"><span class="toc-number">2.1.</span> <span class="toc-text">(1)一对一联结</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E5%A4%9A%E5%AD%97%E6%AE%B5%E8%81%94%E7%BB%93%E5%8F%8A%E4%B8%80%E5%AF%B9%E5%A4%9A"><span class="toc-number">2.2.</span> <span class="toc-text">(2)多字段联结及一对多</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2"><span class="toc-number">3.</span> <span class="toc-text">3.数据类型转换</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E5%BC%BA%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2"><span class="toc-number">3.1.</span> <span class="toc-text">(1)强类型转换</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E6%AE%8B%E7%BC%BA%E6%97%A5%E6%9C%9F%E8%BD%AC%E6%8D%A2"><span class="toc-number">3.2.</span> <span class="toc-text">(2)残缺日期转换</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-%E8%B6%8B%E5%8A%BF%E5%88%86%E6%9E%90"><span class="toc-number">4.</span> <span class="toc-text">4.趋势分析</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E7%89%B9%E5%BE%81%E5%88%86%E7%B1%BB%EF%BC%88%E5%88%86%E9%9D%A2%E6%A3%80%E7%B4%A2%EF%BC%89"><span class="toc-number">4.1.</span> <span class="toc-text">(1)特征分类（分面检索）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E6%9C%80%E5%A4%A7%E5%9B%BE%E7%BD%91%E7%BB%9C"><span class="toc-number">4.2.</span> <span class="toc-text">(2)最大图网络</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E5%A2%9E%E9%87%8F%E5%88%86%E6%9E%90"><span class="toc-number">4.3.</span> <span class="toc-text">(3)增量分析</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8"><span class="toc-number">5.</span> <span class="toc-text">5.数据安全</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E4%B8%A5%E6%A0%BC%E8%A7%86%E5%9B%BE"><span class="toc-number">5.1.</span> <span class="toc-text">(1)严格视图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E9%9A%90%E8%97%8F%E6%95%8F%E6%84%9F%E5%AD%97%E6%AE%B5"><span class="toc-number">5.2.</span> <span class="toc-text">(2)隐藏敏感字段</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-%E6%97%B6%E9%97%B4%E5%BA%8F%E5%88%97"><span class="toc-number">6.</span> <span class="toc-text">6.时间序列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-IOT%E7%94%B5%E5%8A%9B%E6%B6%88%E8%80%97"><span class="toc-number">6.1.</span> <span class="toc-text">(1)IOT电力消耗</span></a></li></ol></li></ol>
    </nav>
  </div>
</aside>

<main class="main" role="main">
  <div class="content">
  <article id="post-mongodb-aggregation-examples" class="article article-type-post" itemscope itemtype="http://schema.org/BlogPosting">
    
    <div class="article-header">
      
        
  
    <h1 class="article-title" itemprop="name">
      MongoDB聚合示例
    </h1>
  

      
      <div class="article-meta">
        <span class="article-date">
    <i class="icon icon-calendar"></i>
	<a href="/20210814/mongodb-aggregation-examples.html" class="article-date">
	  发布于 <time datetime="2021-08-14T15:44:47.000Z" itemprop="datePublished">2021-08-14</time>
	</a>
</span>
<span class="article-date">
    <i class="icon icon-calendar-check"></i>
	<a href="/20210814/mongodb-aggregation-examples.html" class="article-date">
	  更新于 <time datetime="2021-09-30T16:40:26.000Z" itemprop="dateUpdated">2021-10-01</time>
	</a>
</span>
        
  <span class="article-category">
    <i class="icon icon-folder"></i>
    <a class="article-category-link" href="/categories/MongoDB/">MongoDB</a>
  </span>

        
  <span class="article-tag">
    <i class="icon icon-tags"></i>
	<a class="article-tag-link-link" href="/tags/MongoDB/" rel="tag">MongoDB</a>
  </span>


        

	<span class="article-read hidden-xs">
    	<i class="icon icon-eye-fill" aria-hidden="true"></i>
    	<span id="/20210814/mongodb-aggregation-examples.html" class="leancloud_visitors"  data-flag-title="MongoDB聚合示例">
			<span class="leancloud-visitors-count">0</span>
		</span>
    </span>

        <span class="post-comment"><i class="icon icon-comment"></i> <a href="/20210814/mongodb-aggregation-examples.html#comments" class="article-comment-link">评论</a></span>
        
	
		<span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计: 15.9k(字)</span>
	
	
		<span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长: 85(分)</span>
	

      </div>
      <div style="background-color:#D7BDE2;border:1px solid #D7BDE2;border-radius:10px;padding:5px">
          <b>温馨提示</b>：点击页面下方<i style="color:red" class="icon icon-anchor"></i>以展开或折叠目录
      </div>
    </div>
    <div class="article-entry marked-body" itemprop="articleBody">
      
        <blockquote>
<p><font size=5><b>文章说明</b></font><br><strong>文章作者：</strong><a href="https://hwame.top">鴻塵</a><br><strong>文章链接：</strong><a href="https://hwame.top/20210814/mongodb-aggregation-examples.html">https://hwame.top/20210814/mongodb-aggregation-examples.html</a><br><strong>参考资料：</strong></p>
<ul>
<li>聚合示例来源：<a target="_blank" rel="noopener" href="https://www.practical-mongodb-aggregations.com/">Practical MongoDB Aggregations</a>（by Paul Done<a target="_blank" rel="noopener" href="https://twitter.com/TheDonester">@TheDonester</a>）</li>
<li>Ebook源码：<a target="_blank" rel="noopener" href="https://github.com/pkdone/practical-mongodb-aggregations-book">Github @pkdone</a></li>
<li>MongoDB在线运行：<a target="_blank" rel="noopener" href="https://mws.mongodb.com/">MongoDB Web Shell</a></li>
</ul>
<p><strong>温馨提示：</strong>测试数据可直接复制，方便生成数据；运行结果以图片形式展示，可以「点击图片」或「将图片拖曳到新标签页」来查看大图，从而获取更佳的浏览体验。</p>
</blockquote>
<hr>
<p>对MongoDB中聚合操作的理解和熟悉，需要配合大量例子，<a target="_blank" rel="noopener" href="https://www.practical-mongodb-aggregations.com/">Practical MongoDB Aggregations Book</a>中提供了很多有助于理解的例子，但原文是英文版的且有一定概率无法访问，故在此记录下学习过程中对原文的翻译。</p>
<blockquote>
<p><strong>说明：</strong>本文仅作为聚合示例，由于篇幅限制，对于数据库的指定及索引的创建都进行删减，并且也不执行<code>explain</code>。</p>
</blockquote>
<h2 id="1-基础"><a href="#1-基础" class="headerlink" title="1.基础"></a>1.基础</h2><h3 id="1-TopN查询"><a href="#1-TopN查询" class="headerlink" title="(1)TopN查询"></a>(1)TopN查询</h3><blockquote>
<p>您想在由人组成的集合中查询，以找到职业为工程师的三个最年轻的人，按年龄从小到大排序。</p>
<p>此示例是本文中唯一一个可以以完全使用MQL实现的示例，同时也作为MQL和聚合管道之间的一个有用比较。</p>
</blockquote>
<p>测试数据如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;6392529400&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Elise&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Smith&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1972-01-13T09:32:07Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123; <span class="string">&quot;number&quot;</span>: <span class="number">5625</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Tipa Circle&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Wojzinmoj&quot;</span>&#125;,</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;1723338115&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Olive&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Ranieri&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1985-05-12T23:14:30Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123; <span class="string">&quot;number&quot;</span>: <span class="number">9303</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Mele Circle&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Tobihbo&quot;</span>&#125;,</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;8732762874&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Toni&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Jones&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1991-11-23T16:53:56Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;POLITICIAN&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123; <span class="string">&quot;number&quot;</span>: <span class="number">1</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;High Street&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Upper Abbeywoodington&quot;</span>&#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;7363629563&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Bert&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Gooding&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1941-04-07T22:11:52Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;FLORIST&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123; <span class="string">&quot;number&quot;</span>: <span class="number">13</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Upper Bold Road&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Redringtonville&quot;</span>&#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;1029648329&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Sophie&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Celements&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1959-07-06T17:35:45Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123; <span class="string">&quot;number&quot;</span>: <span class="number">5</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Innings Close&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Basilbridge&quot;</span>&#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;7363626383&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Carl&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Simmons&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1998-12-26T13:13:55Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123; <span class="string">&quot;number&quot;</span>: <span class="number">187</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Hillside Road&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Kenningford&quot;</span>&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>聚合管道定义如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">    &#123;<span class="string">&quot;$match&quot;</span>: &#123;<span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,&#125;&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;dateofbirth&quot;</span>: -<span class="number">1</span>,&#125;&#125;,      </span><br><span class="line">    &#123;<span class="string">&quot;$limit&quot;</span>: <span class="number">3</span>&#125;,  </span><br><span class="line">    &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, <span class="string">&quot;vocation&quot;</span>, <span class="string">&quot;address&quot;</span>,]&#125;,</span><br><span class="line">];</span><br></pre></td></tr></table></figure></p>
<p>执行聚合操作后应该返回三个文档，代表三个最年轻的工程师（按年龄从小到大排序），省略每个人的<code>_id</code>或<code>address</code>属性，如图所示：<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-1TopN查询.png" alt="TopN查询"></p>
<ul>
<li><p><strong>Index Use.</strong> A basic aggregation pipeline, where if many records belong to the collection, a compound index for <code>vocation + dateofbirth</code> should exist to enable the database to fully optimise the execution of the pipeline combining the filter of the <code>$match</code> stage with the sort from the <code>sort</code> stage and the limit of the <code>limit</code> stage.</p>
</li>
<li><p><strong>Unset Use.</strong> An <code>$unset</code> stage is used rather than a <code>$project</code> stage. This enables the pipeline to avoid being verbose. More importantly, it means the pipeline does not have to be modified if a new field appears in documents added in the future (for example, see the <code>gender</code> field that appears in only <em>Olive’s</em> record).</p>
</li>
<li><p><strong>MQL Similarity.</strong> For reference, the MQL equivalent for you to achieve the same result is shown below (you can try this in the Shell):</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">db.<span class="property">persons</span>.<span class="title function_">find</span>(</span><br><span class="line">    &#123;<span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>&#125;, &#123;<span class="string">&quot;_id&quot;</span>: <span class="number">0</span>, <span class="string">&quot;vocation&quot;</span>: <span class="number">0</span>, <span class="string">&quot;address&quot;</span>: <span class="number">0</span>&#125;,</span><br><span class="line">  ).<span class="title function_">sort</span>(</span><br><span class="line">    &#123;<span class="string">&quot;dateofbirth&quot;</span>: -<span class="number">1</span>&#125;</span><br><span class="line">  ).<span class="title function_">limit</span>(<span class="number">3</span>);</span><br></pre></td></tr></table></figure>
<h3 id="2-分组汇总"><a href="#2-分组汇总" class="headerlink" title="(2)分组汇总"></a>(2)分组汇总</h3><blockquote>
<p>您想要生成一份报告以显示每个客户在 2020 年购买的商品。</p>
<p>您将按客户对个人订单记录进行分组，捕获每个客户的「首单日期」、「订单数量」、「订单总价」和一个按日期排序的订单项目列表。</p>
</blockquote>
<p>测试数据如下，<code>orders</code>集合由3个不同客户在2019-2021年间的9个订单组成：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-05-30T08:35:52Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;231.43&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-13T09:32:07Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;99.99&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;oranieri@warmmail.com&quot;</span>,   <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-01T08:25:37Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;63.13&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2019-05-28T19:13:32Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;2.01&quot;</span>)&#125;,  </span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-11-23T22:56:53Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;187.99&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-08-18T23:04:48Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.59&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-12-26T08:55:46Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;48.50&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-29T07:49:32Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;1024.89&quot;</span>)&#125;,</span><br><span class="line">&#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-10-03T13:49:44Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;102.24&quot;</span>)&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  &#123;<span class="string">&quot;$match&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;orderdate&quot;</span>: &#123;<span class="string">&quot;$gte&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-01T00:00:00Z&quot;</span>), <span class="string">&quot;$lt&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-01-01T00:00:00Z&quot;</span>), &#125;,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 按订单日期升序，需要选择下面的「first_purchase_date」</span></span><br><span class="line">  &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;orderdate&quot;</span>: <span class="number">1</span>, &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  &#123;<span class="string">&quot;$group&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;_id&quot;</span>: <span class="string">&quot;$customer_id&quot;</span>,</span><br><span class="line">    <span class="string">&quot;first_purchase_date&quot;</span>: &#123;<span class="string">&quot;$first&quot;</span>: <span class="string">&quot;$orderdate&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;total_value&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="string">&quot;$value&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;total_orders&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="number">1</span>&#125;,</span><br><span class="line">    <span class="string">&quot;orders&quot;</span>: &#123;<span class="string">&quot;$push&quot;</span>: &#123;<span class="string">&quot;orderdate&quot;</span>: <span class="string">&quot;$orderdate&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="string">&quot;$value&quot;</span>&#125;&#125;,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line">  </span><br><span class="line">  &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;first_purchase_date&quot;</span>: <span class="number">1</span>, &#125;&#125;,    </span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;$_id&quot;</span>, &#125;&#125;,</span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, ]&#125;,   </span><br><span class="line">];</span><br></pre></td></tr></table></figure></p>
<p>执行聚合操作后应该返回代表三个客户的三个文档，文档分别包含了2020年首次购买日期、订单总价值、订单数量，以及订单详情列表。<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-2分组汇总.png" alt="分组汇总"></p>
<p>Note, the order of fields shown for each document may vary.</p>
<ul>
<li><p><strong>Double Sort Use.</strong> It is necessary to perform a <code>$sort</code> on the order date both before and after the <code>$group</code> stage. The <code>$sort</code> before the <code>$group</code> is required because the <code>$group</code> stage uses a <code>$first</code> group accumulator to capture just the first order’s <code>orderdate</code> value for each grouped customer. The <code>$sort</code> after the <code>$group</code> is required because the act of having just grouped on customer ID will mean that the records are no longer sorted by purchase date for the records coming out of the <code>$group</code> stage.</p>
</li>
<li><p><strong>Renaming Group.</strong> Towards the end of the pipeline, you will see what is a typical pattern for pipelines that use <code>$group</code>, consisting of a combination of <code>$set</code>+<code>$unset</code> stages, to essentially take the group’s key (which is always called <code>_id</code>) and substitute it with a more meaningful name (<code>customer_id</code>).</p>
</li>
<li><p><strong>Lossless Decimals.</strong> You may notice the pipeline uses a <code>NumberDecimal()</code> function to ensure the order amounts in the inserted records are using a lossless decimal type, <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/tutorial/model-monetary-data/">IEEE 754 decimal128</a>. In this example, if you use a JSON <em>float</em> or <em>double</em> type instead, the order totals will suffer from a loss of precision. For instance, for the customer <code>elise_smith@myemail.com</code>, if you use a <em>double</em> type, the <code>total_value</code> result will have the value shown in the second line below, rather than the first line:</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用decimal128类型获得的期望结果</span></span><br><span class="line"><span class="attr">total_value</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&#x27;482.16&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用float或double类型获得的结果</span></span><br><span class="line"><span class="attr">total_value</span>: <span class="number">482.15999999999997</span></span><br></pre></td></tr></table></figure>
<h3 id="3-解包数组及分组"><a href="#3-解包数组及分组" class="headerlink" title="(3)解包数组及分组"></a>(3)解包数组及分组</h3><blockquote>
<p>您想要生成零售报告以列出已售出「昂贵产品」（价值超过 15 美元）的总价值和数量。</p>
<p>数据源是一个商店订单列表，其中每个订单都包含一个购买的产品集。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;order_id&quot;</span>: <span class="number">6363763262239</span>,</span><br><span class="line">    <span class="string">&quot;products&quot;</span>: [</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;abc12345&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;431.43&quot;</span>), &#125;,</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;def45678&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Karcher Hose Set&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;22.13&quot;</span>), &#125;,</span><br><span class="line">    ],</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;order_id&quot;</span>: <span class="number">1197372932325</span>,</span><br><span class="line">    <span class="string">&quot;products&quot;</span>: [</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;abc12345&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;429.99&quot;</span>), &#125;,</span><br><span class="line">    ],</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;order_id&quot;</span>: <span class="number">9812343774839</span>,</span><br><span class="line">    <span class="string">&quot;products&quot;</span>: [</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;pqr88223&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Morphy Richardds Food Mixer&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;431.43&quot;</span>), &#125;,</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;def45678&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Karcher Hose Set&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;21.78&quot;</span>), &#125;,</span><br><span class="line">    ],</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;order_id&quot;</span>: <span class="number">4433997244387</span>,</span><br><span class="line">    <span class="string">&quot;products&quot;</span>: [</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;def45678&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Karcher Hose Set&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;23.43&quot;</span>), &#125;,</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;jkl77336&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Picky Pencil Sharpener&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;0.67&quot;</span>), &#125;,</span><br><span class="line">      &#123;<span class="string">&quot;prod_id&quot;</span>: <span class="string">&quot;xyz11228&quot;</span>, <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Russell Hobbs Chrome Kettle&quot;</span>, <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;15.76&quot;</span>), &#125;,</span><br><span class="line">    ],</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  <span class="comment">// 从每个订单的products中解包每个product，作为一个新的单独记录</span></span><br><span class="line">  &#123;<span class="string">&quot;$unwind&quot;</span>: &#123;<span class="string">&quot;path&quot;</span>: <span class="string">&quot;$products&quot;</span>, &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  &#123;<span class="string">&quot;$match&quot;</span>: &#123;<span class="string">&quot;products.price&quot;</span>: &#123; <span class="string">&quot;$gt&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;15.00&quot;</span>), &#125;,&#125;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 按产品类型分组，统计每个产品的总价和数量</span></span><br><span class="line">  &#123;<span class="string">&quot;$group&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;_id&quot;</span>: <span class="string">&quot;$products.prod_id&quot;</span>,</span><br><span class="line">    <span class="string">&quot;product&quot;</span>: &#123;<span class="string">&quot;$first&quot;</span>: <span class="string">&quot;$products.name&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;total_value&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="string">&quot;$products.price&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;quantity&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="number">1</span>&#125;,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;product_id&quot;</span>: <span class="string">&quot;$_id&quot;</span>, &#125;&#125;,</span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>,]&#125;,</span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后应该返回4个文档，代表客户订单中出现次数最多的4个仅有的「昂贵产品」，每个文档包括了产品的总订单价值和数量，如下所示：<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-3解包分组.png" alt="解包分组"></p>
<p>Note, the order of fields shown for each document may vary.</p>
<ul>
<li><p><strong>Unwinding Arrays.</strong> The <code>$unwind</code> stage is a powerful concept, although often unfamiliar to many developers initially. Distilled down, it does one simple thing: it generates a new record for each element in an array field of every input document. If a source collection has 3 documents and each document contains an array of 4 elements, then performing an <code>$unwind</code> on each record’s array field produces 12 records (3 x 4).</p>
</li>
<li><p><strong>Introducing A Partial Match</strong>. The current example pipeline scans all documents in the collection and then filters out unpacked products where <code>price &gt; 15.00</code>. If the pipeline executed this filter as the first stage, it would incorrectly produce some result product records with a value of 15 dollars or less. This would be the case for an order composed of both inexpensive and expensive products. However, you can still improve the pipeline by including an additional “partial match” filter at the start of the pipeline for products valued at over 15 dollars. The aggregation could leverage an index (on <code>products.price</code>), resulting in a partial rather than full collection scan. This extra filter stage is beneficial if the input data set is large and many customer orders are for inexpensive items only. This approach is described in the chapter <a href="../../guides/performance.md#partial_match">Pipeline Performance Considerations</a>.</p>
</li>
</ul>
<h3 id="4-列表去重"><a href="#4-列表去重" class="headerlink" title="(4)列表去重"></a>(4)列表去重</h3><blockquote>
<p>您想在persons集合中查询，其中每个文档都包含其所说的一种或多种语言。</p>
<p>查询结果应该是按字母顺序排序的去重的语言列表，开发人员随后可以使用它来填充用户界面下拉小部件中的值列表。</p>
<p>此示例等效于<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/SQL">SQL</a>中的<code>SELECT DISTINCT</code>语句。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Elise&quot;</span>,   <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Smith&quot;</span>,     <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,   <span class="string">&quot;language&quot;</span>: <span class="string">&quot;English&quot;</span>,&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Olive&quot;</span>,   <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Ranieri&quot;</span>,   <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,   <span class="string">&quot;language&quot;</span>: [<span class="string">&quot;Italian&quot;</span>, <span class="string">&quot;English&quot;</span>],&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Toni&quot;</span>,    <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Jones&quot;</span>,     <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;POLITICIAN&quot;</span>, <span class="string">&quot;language&quot;</span>: [<span class="string">&quot;English&quot;</span>, <span class="string">&quot;Welsh&quot;</span>],&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Bert&quot;</span>,    <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Gooding&quot;</span>,   <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;FLORIST&quot;</span>,    <span class="string">&quot;language&quot;</span>: <span class="string">&quot;English&quot;</span>,&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Sophie&quot;</span>,  <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Celements&quot;</span>, <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,   <span class="string">&quot;language&quot;</span>: [<span class="string">&quot;Gaelic&quot;</span>, <span class="string">&quot;English&quot;</span>],&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Carl&quot;</span>,    <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Simmons&quot;</span>,   <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;ENGINEER&quot;</span>,   <span class="string">&quot;language&quot;</span>: <span class="string">&quot;English&quot;</span>,&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Diego&quot;</span>,   <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Lopez&quot;</span>,     <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;CHEF&quot;</span>,       <span class="string">&quot;language&quot;</span>: <span class="string">&quot;Spanish&quot;</span>,&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Helmut&quot;</span>,  <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Schneider&quot;</span>, <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;NURSE&quot;</span>,      <span class="string">&quot;language&quot;</span>: <span class="string">&quot;German&quot;</span>,&#125;,</span><br><span class="line">&#123;<span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Valerie&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Dubois&quot;</span>,    <span class="string">&quot;vocation&quot;</span>: <span class="string">&quot;SCIENTIST&quot;</span>,  <span class="string">&quot;language&quot;</span>: <span class="string">&quot;French&quot;</span>,&#125;,</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  &#123;<span class="string">&quot;$unwind&quot;</span>: &#123;<span class="string">&quot;path&quot;</span>: <span class="string">&quot;$language&quot;</span>&#125;&#125;, <span class="comment">// 解包language字段，该字段为数组或单个值</span></span><br><span class="line">  &#123;<span class="string">&quot;$group&quot;</span>: &#123;<span class="string">&quot;_id&quot;</span>: <span class="string">&quot;$language&quot;</span>&#125;&#125;,</span><br><span class="line">  &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;_id&quot;</span>: <span class="number">1</span>&#125;&#125;, </span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;language&quot;</span>: <span class="string">&quot;$_id&quot;</span>, <span class="string">&quot;_id&quot;</span>: <span class="string">&quot;$$REMOVE&quot;</span>&#125;&#125;,</span><br><span class="line">];</span><br></pre></td></tr></table></figure></p>
<p>执行聚合操作后应该返回按字母序的7个文档，代表不同的7种语言，如下所示：<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-4列表去重.png" alt="列表去重"></p>
<ul>
<li><p><strong>Unwinding Non-Arrays.</strong> In some of the example’s documents, the <code>language</code> field is an array, whilst in others, the field is a simple string value. The <code>$unwind</code> stage can seamlessly deal with both field types and does not throw an error if it encounters a non-array value. Instead, if the field is not an array, the stage outputs a single record using the field’s string value in the same way it would if the field was an array containing just one element. If you are sure the field in every document will only ever be a simple field rather than an array, you can omit this first stage (<code>$unwind</code>) from the pipeline.</p>
</li>
<li><p><strong>Group ID Provides Unique Values.</strong> By grouping on a single field and not accumulating other fields such as total or count, the output of a <code>$group</code> stage is just every unique group’s ID, which in this case is every unique language.</p>
</li>
<li><p><strong>Unset Alternative.</strong> For the pipeline to be consistent with earlier examples in this book, it could have included an additional <code>$unset</code> stage to exclude the <code>_id</code> field. However, partly to show another way, the example pipeline used here marks the <code>_id</code> field for exclusion in the <code>$set</code> stage by being assigned the <code>$$REMOVE</code> variable.</p>
</li>
</ul>
<h2 id="2-数据联结"><a href="#2-数据联结" class="headerlink" title="2.数据联结"></a>2.数据联结</h2><h3 id="1-一对一联结"><a href="#1-一对一联结" class="headerlink" title="(1)一对一联结"></a>(1)一对一联结</h3><blockquote>
<p>您想要生成一个报告来列出 2020 年所有的购买情况，显示每个订单的产品名称和类别，而不是产品ID。</p>
<p>为此您需要获取客户「订单集合<code>orders</code>」，并将每个订单记录与「产品集合<code>products</code>」中的对应的产品相<strong>联结</strong>。</p>
<p>两个集合之间存在「多对一<code>many:1</code>」关系，因此在将订单与产品匹配时会产生「一对一<code>1:1</code>」的联结。联结将根据产品的<code>id</code>在双方之间使用单个字段比较。</p>
<p>数据包括2019-2021年间的产品<code>products</code>和订单<code>orders</code>两个集合，如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 集合products</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;id&quot;</span>: <span class="string">&quot;a1b2c3d4&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;ELECTRONICS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Good value laptop for students&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;id&quot;</span>: <span class="string">&quot;z9y8x7w6&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;The Day Of The Triffids&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;BOOKS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Classic post-apocalyptic novel&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;id&quot;</span>: <span class="string">&quot;ff11gg22hh33&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Morphy Richardds Food Mixer&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;KITCHENWARE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Luxury mixer turning good cakes into great&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;id&quot;</span>: <span class="string">&quot;pqr678st&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Karcher Hose Set&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;GARDEN&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Hose + nosels + winder for tidy storage&quot;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 集合orders</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-05-30T08:35:52Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_id&quot;</span>: <span class="string">&quot;a1b2c3d4&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;431.43&quot;</span>),</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2019-05-28T19:13:32Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_id&quot;</span>: <span class="string">&quot;z9y8x7w6&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;5.01&quot;</span>),</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;oranieri@warmmail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-01T08:25:37Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_id&quot;</span>: <span class="string">&quot;ff11gg22hh33&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;63.13&quot;</span>),</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;jjones@tepidmail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-12-26T08:55:46Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_id&quot;</span>: <span class="string">&quot;a1b2c3d4&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;429.65&quot;</span>),</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  &#123;<span class="string">&quot;$match&quot;</span>: &#123;<span class="string">&quot;orderdate&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;$gte&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-01T00:00:00Z&quot;</span>), <span class="string">&quot;$lt&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-01-01T00:00:00Z&quot;</span>),</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Join &quot;product_id&quot; in orders collection to &quot;id&quot; in products&quot; collection</span></span><br><span class="line">  &#123;<span class="string">&quot;$lookup&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;from&quot;</span>: <span class="string">&quot;products&quot;</span>, <span class="string">&quot;localField&quot;</span>: <span class="string">&quot;product_id&quot;</span>, <span class="string">&quot;foreignField&quot;</span>: <span class="string">&quot;id&quot;</span>, <span class="string">&quot;as&quot;</span>: <span class="string">&quot;product_mapping&quot;</span>,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">// For this data model, will always be 1 record in right-side</span></span><br><span class="line">  <span class="comment">// of join, so take 1st joined array element</span></span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;product_mapping&quot;</span>: &#123;<span class="string">&quot;$first&quot;</span>: <span class="string">&quot;$product_mapping&quot;</span>&#125;, &#125;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Extract the joined embeded fields into top level fields</span></span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;product_name&quot;</span>: <span class="string">&quot;$product_mapping.name&quot;</span>, <span class="string">&quot;product_category&quot;</span>: <span class="string">&quot;$product_mapping.category&quot;</span>,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line">  </span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, <span class="string">&quot;product_id&quot;</span>, <span class="string">&quot;product_mapping&quot;</span>, ]&#125;,     </span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后应该返回代表2020年的3个订单文档，但每个订单的<code>product_id</code>字段替换成了两个新查找的字段<code>product_name</code>和<code>product_category</code>，如下所示。<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-5一对一联结.png" alt="一对一联结"></p>
<ul>
<li><p><strong>Single Field Match.</strong> The pipeline includes a <code>$lookup</code> join between a single field from each collection. For an illustration of performing a join based on two or more matching fields, see the <a href="./multi-one-to-many.html">Multi-Field Join &amp; One-to-Many</a> example.</p>
</li>
<li><p><strong>First Element Assumption.</strong> In this particular data model example, the join between the two collections is 1:1. Therefore the returned array of joined elements coming out of the <code>$lookup</code> stage always contains precisely one array element. As a result, the pipeline extracts the data from this first array element only, using the <code>$first</code> operator. For an illustration of performing a 1:many join instead, see the <a href="./multi-one-to-many.html">Multi-Field Join &amp; One-to-Many</a> example.</p>
</li>
</ul>
<h3 id="2-多字段联结及一对多"><a href="#2-多字段联结及一对多" class="headerlink" title="(2)多字段联结及一对多"></a>(2)多字段联结及一对多</h3><blockquote>
<p>您想要生成一份报告，列出2020年每种产品所生成的所有订单。</p>
<p>为此，您需要将商店的「产品集合<code>products</code>」中的每个产品<code>product</code>，与产品对应的订单<code>order</code>（订单位于<code>orders</code>集合）相联结。</p>
<p>基于每侧两个字段的匹配，两个集合之间存在「一对多<code>1:many</code>」的关系。</p>
<p>联结需要使用两个公共字段（<code>product_name</code>和<code>product_variation</code>），而不是像<code>product_id</code>（此数据集中不存在该字段）这样的单个字段。</p>
<p><strong>注意：</strong>执行一对多<code>1:many</code>的联结并不强制要求在每一侧通过多个字段进行联结。然而在这个例子中，在一个地方展示这两个方面被认为是有益的。</p>
<p>数据包括2019-2021年间的产品<code>products</code>和订单<code>orders</code>两个集合，如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 集合products</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>,</span><br><span class="line">    <span class="string">&quot;variation&quot;</span>: <span class="string">&quot;Ultra HD&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;ELECTRONICS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Great for watching movies&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>,</span><br><span class="line">    <span class="string">&quot;variation&quot;</span>: <span class="string">&quot;Normal Display&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;ELECTRONICS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Good value laptop for students&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;The Day Of The Triffids&quot;</span>,</span><br><span class="line">    <span class="string">&quot;variation&quot;</span>: <span class="string">&quot;1st Edition&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;BOOKS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Classic post-apocalyptic novel&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;The Day Of The Triffids&quot;</span>,</span><br><span class="line">    <span class="string">&quot;variation&quot;</span>: <span class="string">&quot;2nd Edition&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;BOOKS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Classic post-apocalyptic novel&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Morphy Richards Food Mixer&quot;</span>,</span><br><span class="line">    <span class="string">&quot;variation&quot;</span>: <span class="string">&quot;Deluxe&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;KITCHENWARE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Luxury mixer turning good cakes into great&quot;</span>,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Karcher Hose Set&quot;</span>,</span><br><span class="line">    <span class="string">&quot;variation&quot;</span>: <span class="string">&quot;Full Monty&quot;</span>,</span><br><span class="line">    <span class="string">&quot;category&quot;</span>: <span class="string">&quot;GARDEN&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Hose + nosels + winder for tidy storage&quot;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 集合orders</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-05-30T08:35:52Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>, <span class="string">&quot;product_variation&quot;</span>: <span class="string">&quot;Normal Display&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;431.43&quot;</span>),</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2019-05-28T19:13:32Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_name&quot;</span>: <span class="string">&quot;The Day Of The Triffids&quot;</span>, <span class="string">&quot;product_variation&quot;</span>: <span class="string">&quot;2nd Edition&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;5.01&quot;</span>),</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;oranieri@warmmail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-01T08:25:37Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_name&quot;</span>: <span class="string">&quot;Morphy Richards Food Mixer&quot;</span>, <span class="string">&quot;product_variation&quot;</span>: <span class="string">&quot;Deluxe&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;63.13&quot;</span>),</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;jjones@tepidmail.com&quot;</span>, <span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-12-26T08:55:46Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;product_name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>, <span class="string">&quot;product_variation&quot;</span>: <span class="string">&quot;Normal Display&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;429.65&quot;</span>),</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  <span class="comment">// 将产品集合中的 2 个字段连接到订单集合中的 2 个字段</span></span><br><span class="line">  &#123;<span class="string">&quot;$lookup&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;from&quot;</span>: <span class="string">&quot;orders&quot;</span>,</span><br><span class="line">    <span class="string">&quot;let&quot;</span>: &#123;<span class="string">&quot;prdname&quot;</span>: <span class="string">&quot;$name&quot;</span>, <span class="string">&quot;prdvartn&quot;</span>: <span class="string">&quot;$variation&quot;</span>, &#125;,</span><br><span class="line">    <span class="comment">// Embedded pipeline to control how the join is matched</span></span><br><span class="line">    <span class="string">&quot;pipeline&quot;</span>: [</span><br><span class="line">      <span class="comment">// Join by two fields in each side</span></span><br><span class="line">      &#123;<span class="string">&quot;$match&quot;</span>:</span><br><span class="line">        &#123;<span class="string">&quot;$expr&quot;</span>: &#123;<span class="string">&quot;$and&quot;</span>: [</span><br><span class="line">            &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$product_name&quot;</span>, <span class="string">&quot;$$prdname&quot;</span>]&#125;, &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$product_variation&quot;</span>, <span class="string">&quot;$$prdvartn&quot;</span>]&#125;,</span><br><span class="line">          ]&#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line"></span><br><span class="line">      <span class="comment">// Match only orders made in 2020</span></span><br><span class="line">      &#123;<span class="string">&quot;$match&quot;</span>: &#123;<span class="string">&quot;orderdate&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;$gte&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-01-01T00:00:00Z&quot;</span>), <span class="string">&quot;$lt&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-01-01T00:00:00Z&quot;</span>),</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;&#125;,</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// Exclude some unwanted fields from the right side of the join</span></span><br><span class="line">      &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, <span class="string">&quot;product_name&quot;</span>, <span class="string">&quot;product_variation&quot;</span>, ]&#125;,</span><br><span class="line">    ],</span><br><span class="line">    <span class="attr">as</span>: <span class="string">&quot;orders&quot;</span>,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Only show products that have at least one order</span></span><br><span class="line">  &#123;<span class="string">&quot;$match&quot;</span>: &#123;<span class="string">&quot;orders&quot;</span>: &#123;<span class="string">&quot;$ne&quot;</span>: []&#125;,&#125;&#125;,</span><br><span class="line"></span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>,]&#125;, </span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后应该返回2个文档，代表 2020 年有一个或多个订单的两个产品，每个产品对应的订单以数组形式展示，如下所示。<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-6多字段联结及一对多.png" alt="多字段联结及一对多"></p>
<ul>
<li><p><strong>Multiple Join Fields.</strong> To perform a join of two or more fields between the two collections, you need to use a <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#join-conditions-and-uncorrelated-sub-queries">let</a> parameter rather than specifying the <code>localField</code> and <code>foreignField</code> parameters used in a single field join. With a <code>let</code> parameter, you bind multiple fields from the first collection into variables ready to be used in the joining process. You use an embedded <code>pipeline</code> inside the <code>$lookup</code> stage to match the <em>bind</em> variables with fields in the second collection’s records. In this instance,  because the <code>$expr</code> operator performs an equality comparison specifically (as opposed to a range comparison), the aggregation runtime can employ an appropriate index for this match.</p>
</li>
<li><p><strong>Reducing Array Content.</strong> The presence of an embedded pipeline in the <code>$lookup</code> stage provides an opportunity to filter out three unwanted fields brought in from the second collection. Instead, you could use an <code>$unset</code> stage later in the top-level pipeline to project out these unwanted array elements. If you need to perform more complex array content filtering rules, you can use the approach described in section <em><a href="../../guides/performance.md#avoid_unwinding">2. Avoid Unwinding &amp; Regrouping Documents Just To Process Array Elements</a></em>“ of the <em>Pipeline Performance Considerations</em> chapter.</p>
</li>
</ul>
<h2 id="3-数据类型转换"><a href="#3-数据类型转换" class="headerlink" title="3.数据类型转换"></a>3.数据类型转换</h2><h3 id="1-强类型转换"><a href="#1-强类型转换" class="headerlink" title="(1)强类型转换"></a>(1)强类型转换</h3><blockquote>
<p>一组零售订单<em>retail orders</em>已被某三方机构导入MongoDB集合中，但是所有数据类型都丢失了，因此所有字段值被存储为字符串形式。</p>
<p>您想为所有文档重新建立正确的数据，并将它们复制到一个新的「清理过的」集合中。您可以在聚合管道中包含该类型的转换逻辑，因为您知道每个字段在原始记录中的结构类型。</p>
<p>与本文中的大多数示例不同，聚合管道将其输出写入一个集合，而不是将结果流式地返回给调用的应用程序。</p>
<p>订单集合<code>orders</code>包含三个订单文档，其中每个订单只有字符串形式的<strong>文本字段</strong>（注意，第二个文档有意缺少<code>further_info</code>子文档中的<code>reported</code>字段），如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;order_date&quot;</span>: <span class="string">&quot;2020-05-30T08:35:52&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="string">&quot;231.43&quot;</span>,</span><br><span class="line">    <span class="string">&quot;further_info&quot;</span>: &#123;<span class="string">&quot;item_qty&quot;</span>: <span class="string">&quot;3&quot;</span>, <span class="string">&quot;reported&quot;</span>: <span class="string">&quot;false&quot;</span>,&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;oranieri@warmmail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;order_date&quot;</span>: <span class="string">&quot;2020-01-01T08:25:37&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="string">&quot;63.13&quot;</span>,</span><br><span class="line">    <span class="string">&quot;further_info&quot;</span>: &#123;<span class="string">&quot;item_qty&quot;</span>: <span class="string">&quot;2&quot;</span>,&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;customer_id&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;order_date&quot;</span>: <span class="string">&quot;2019-05-28T19:13:32&quot;</span>,</span><br><span class="line">    <span class="string">&quot;value&quot;</span>: <span class="string">&quot;2.01&quot;</span>,</span><br><span class="line">    <span class="string">&quot;further_info&quot;</span>: &#123;<span class="string">&quot;item_qty&quot;</span>: <span class="string">&quot;1&quot;</span>, <span class="string">&quot;reported&quot;</span>: <span class="string">&quot;true&quot;</span>, &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  <span class="comment">// 将字符串转换为所需类型</span></span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;order_date&quot;</span>: &#123;<span class="string">&quot;$toDate&quot;</span>: <span class="string">&quot;$order_date&quot;</span>&#125;,    </span><br><span class="line">    <span class="string">&quot;value&quot;</span>: &#123;<span class="string">&quot;$toDecimal&quot;</span>: <span class="string">&quot;$value&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;further_info.item_qty&quot;</span>: &#123;<span class="string">&quot;$toInt&quot;</span>: <span class="string">&quot;$further_info.item_qty&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;further_info.reported&quot;</span>: &#123;<span class="string">&quot;$switch&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;branches&quot;</span>: [</span><br><span class="line">        &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [&#123;<span class="string">&quot;$toLower&quot;</span>: <span class="string">&quot;$further_info.reported&quot;</span>&#125;, <span class="string">&quot;true&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="literal">true</span>&#125;,</span><br><span class="line">        &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [&#123;<span class="string">&quot;$toLower&quot;</span>: <span class="string">&quot;$further_info.reported&quot;</span>&#125;, <span class="string">&quot;false&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="literal">false</span>&#125;,</span><br><span class="line">      ],</span><br><span class="line">      <span class="string">&quot;default&quot;</span>: &#123;<span class="string">&quot;$ifNull&quot;</span>: [<span class="string">&quot;$further_info.reported&quot;</span>, <span class="string">&quot;$$REMOVE&quot;</span>]&#125;,</span><br><span class="line">    &#125;&#125;,     </span><br><span class="line">  &#125;&#125;,     </span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 输出到未分片（unsharded）或分片（sharded）的集合</span></span><br><span class="line">  &#123;<span class="string">&quot;$merge&quot;</span>: &#123;<span class="string">&quot;into&quot;</span>: <span class="string">&quot;orders_typed&quot;</span>, &#125;&#125;,    </span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后将生成一个新的名为<code>orders_typed</code>的集合，新集合<code>orders_typed</code>应该与原集合<code>orders</code>具有相同数量的文档、相同的字段结构和字段名称，但现在使用了合适的强类型的<strong>布尔值</strong>、<strong>日期</strong>、<strong>整数</strong>和<strong>十进制值</strong>，集合内容如下所示。</p>
<p><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-7强类型转换.png" alt="强类型转换"></p>
<ul>
<li><p><strong>Boolean Conversion.</strong> The pipeline’s conversions for integers, decimals, and dates are straightforward using the corresponding expression operators, <code>$toInt</code>, <code>$toDecimal</code> and <code>$toDate</code>. However, the expression operator <code>$toBool</code> is not used for the boolean conversion. This is because <code>$toBool</code> will convert any non-empty string to <em>true</em> regardless of its value. As a result, the pipeline uses a <code>$switch</code> operator to compare the lowercase version of strings with the text <code>&#39;true&#39;</code> and <code>&#39;false&#39;</code>, returning the matching boolean.</p>
</li>
<li><p><strong>Preserving Non-Existence.</strong> The field <code>further_info.reported</code> is an optional field in this scenario. The field may not always appear in a document, as illustrated by one of the three documents in the example. If a field is not present in a document, this potentially significant fact should never be lost. The pipeline includes additional logic for the <code>further_info.reported</code> field to preserve this information. The pipeline ensures the field is not included in the output document if it didn’t exist in the source document. A <code>$ifNull</code> conditional operator is used, which returns the <code>$$REMOVE</code> marker flag if the field is missing, instructing the aggregation engine to omit it.</p>
</li>
<li><p><strong>Output To A Collection.</strong> The pipeline uses a <code>$merge</code> stage to instruct the aggregation engine to write the output to a collection rather than returning a stream of results. For this example, the default settings for <code>$merge</code> are sufficient. Each transformed record coming out of the aggregation pipeline becomes a new record in the target collection. The pipeline could have used a <code>$out</code> rather than a <code>$merge</code> stage. However, because <code>$merge</code> supports both unsharded and sharded collections, whereas <code>$out</code> only supports the former, <code>$merge</code> provides a more universally applicable example. If your aggregation needs to create a brand new unsharded collection, <code>$out</code> may be a little faster because the aggregation will completely replace the existing collection if it exists. Using <code>$merge</code>, the system has to perform more checks for every record the aggregation inserts (even though, in this case, it will be to a new collection).</p>
</li>
<li><p><strong>Trickier Date Conversions.</strong> In this example, the date strings contain all the date parts required by the <code>$toDate</code> operator to perform a conversion correctly. In some situations, this may not be the case, and a date string may be missing some valuable information (e.g. which century a 2-character year string is for, such as the century <code>19</code> or <code>21</code>). To understand how to deal with these cases, see the <a href="./convert-incomplete-dates.md">Convert Incomplete Date Strings</a> example chapter.</p>
</li>
</ul>
<h3 id="2-残缺日期转换"><a href="#2-残缺日期转换" class="headerlink" title="(2)残缺日期转换"></a>(2)残缺日期转换</h3><blockquote>
<p>应用程序正在将<code>payment</code>文档提取到 MongoDB 集合中，其中每个文档的<code>payment date</code>字段都包含一个「看起来有点像日期时间」的字符串，例如<code>&quot;01-JAN-20 01.01.01.123000000&quot;</code>。</p>
<p>您希望在聚合时将每个<code>payment date</code>转换为有效的<code>BSON</code>日期类型，但是<strong>该字段不包含能准确确定确切日期时间所需的所有信息</strong>。因此，您不能直接使用 MongoDB 的<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/#date-expression-operators">日期表达式运算符</a>将文本转换为日期。</p>
<p>这些文本字段中的每一个都缺少以下信息：</p>
<ul>
<li>明确的<strong>世纪</strong>：例如是1900s，还是2000s，还是其他；</li>
<li>明确的<strong>时区</strong>：例如是GMT，是IST，还是PST，还是其他；</li>
<li>三个字母的月份缩写所代表的具体<strong>语言</strong>：例如「JAN」是法语，还是英语，还是其他。</li>
</ul>
<p>假设您随后了解到所有付款记录仅发生在21世纪，提取数据时使用的时区是UTC，使用的语言是英语。有了这些信息，您就可以构建一个聚合管道来将这些文本字段转换为日期字段。</p>
<p>缴费集合<code>payments</code>包含12个支付文档，包括了时间乱序的覆盖了2020年所有12个月的数据，如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;010101&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;01-JAN-20 01.01.01.123000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">1.01</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;020202&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;02-FEB-20 02.02.02.456000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">2.02</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;030303&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;03-MAR-20 03.03.03.789000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">3.03</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;040404&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;04-APR-20 04.04.04.012000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">4.04</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;050505&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;05-MAY-20 05.05.05.345000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">5.05</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;060606&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;06-JUN-20 06.06.06.678000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">6.06</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;070707&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;07-JUL-20 07.07.07.901000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">7.07</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;080808&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;08-AUG-20 08.08.08.234000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">8.08</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;090909&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;09-SEP-20 09.09.09.567000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">9.09</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;101010&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;10-OCT-20 10.10.10.890000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">10.10</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;111111&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;11-NOV-20 11.11.11.111000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">11.11</span>&#125;,</span><br><span class="line">&#123;<span class="string">&quot;account&quot;</span>: <span class="string">&quot;121212&quot;</span>, <span class="string">&quot;payment_date&quot;</span>: <span class="string">&quot;12-DEC-20 12.12.12.999000000&quot;</span>, <span class="string">&quot;amount&quot;</span>: <span class="number">12.12</span>&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  <span class="comment">// 将字段从字符串转换为日期类型，填补缺失的空白</span></span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;payment_date&quot;</span>: &#123;    </span><br><span class="line">      <span class="string">&quot;$let&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;vars&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;txt&quot;</span>: <span class="string">&quot;$payment_date&quot;</span>,  <span class="comment">// Assign &quot;payment_date&quot; field to variable &quot;txt&quot;,</span></span><br><span class="line">          <span class="string">&quot;month&quot;</span>: &#123;<span class="string">&quot;$substrCP&quot;</span>: [<span class="string">&quot;$payment_date&quot;</span>, <span class="number">3</span>, <span class="number">3</span>]&#125;,  <span class="comment">// Extract month text</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;in&quot;</span>: &#123; </span><br><span class="line">          <span class="string">&quot;$dateFromString&quot;</span>: &#123;<span class="string">&quot;format&quot;</span>: <span class="string">&quot;%d-%m-%Y %H.%M.%S.%L&quot;</span>, <span class="string">&quot;dateString&quot;</span>:</span><br><span class="line">            &#123;<span class="string">&quot;$concat&quot;</span>: [</span><br><span class="line">              &#123;<span class="string">&quot;$substrCP&quot;</span>: [<span class="string">&quot;$$txt&quot;</span>, <span class="number">0</span>, <span class="number">3</span>]&#125;,  <span class="comment">// Use 1st 3 chars in string</span></span><br><span class="line">              &#123;<span class="string">&quot;$switch&quot;</span>: &#123;<span class="string">&quot;branches&quot;</span>: [  <span class="comment">// Replace month 3 chars with month number</span></span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;JAN&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;01&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;FEB&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;02&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;MAR&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;03&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;APR&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;04&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;MAY&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;05&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;JUN&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;06&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;JUL&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;07&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;AUG&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;08&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;SEP&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;09&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;OCT&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;10&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;NOV&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;11&quot;</span>&#125;,</span><br><span class="line">                &#123;<span class="string">&quot;case&quot;</span>: &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$$month&quot;</span>, <span class="string">&quot;DEC&quot;</span>]&#125;, <span class="string">&quot;then&quot;</span>: <span class="string">&quot;12&quot;</span>&#125;,</span><br><span class="line">               ], <span class="string">&quot;default&quot;</span>: <span class="string">&quot;ERROR&quot;</span>&#125;&#125;,</span><br><span class="line">              <span class="string">&quot;-20&quot;</span>,  <span class="comment">// Add hyphen + hardcoded century 2 digits</span></span><br><span class="line">              &#123;<span class="string">&quot;$substrCP&quot;</span>: [<span class="string">&quot;$$txt&quot;</span>, <span class="number">7</span>, <span class="number">15</span>]&#125;  <span class="comment">// Use remaining 3 millis (ignore last 6 nanosecs)</span></span><br><span class="line">            ]</span><br><span class="line">          &#125;&#125;                  </span><br><span class="line">        &#125;</span><br><span class="line">      &#125;        </span><br><span class="line">    &#125;,             </span><br><span class="line">  &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, ]&#125;,         </span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后应该返回对应于原文档的12个文档，但将<code>payment_date</code>从文本值转换为正确的日期类型，如下所示。</p>
<p><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-8残缺日期转换.png" alt="残缺日期转换"></p>
<ul>
<li><p><strong>Concatenation Explanation.</strong> In this pipeline, the text fields (e.g. <code>&#39;12-DEC-20 12.12.12.999000000&#39;</code>) are each converted to date fields (e.g. <code>2020-12-12T12:12:12.999Z</code>). This is achieved by concatenating together the following four example elements before passing them to the <code>$dateFromString</code> operator to convert to a date type:</p>
<ul>
<li><code>&#39;12-&#39;</code> <em>(day of the month from the input string + the hyphen suffix already present in the text)</em></li>
<li><code>&#39;12&#39;</code> <em>(replacing ‘DEC’)</em></li>
<li><code>&#39;-20&#39;</code> <em>(hard-coded hyphen + hardcoded century)</em></li>
<li><code>&#39;20 12.12.12.999&#39;</code> <em>(the rest of input string apart from the last 6 nanosecond digits)</em></li>
</ul>
</li>
<li><p><strong>Further Reading.</strong> This example is based on the output of the blog post: <a target="_blank" rel="noopener" href="https://pauldone.blogspot.com/2020/05/aggregation-convert-nasty-date-strings.html">Converting Gnarly Date Strings to Proper Date Types Using a MongoDB Aggregation Pipeline</a>.</p>
</li>
</ul>
<h2 id="4-趋势分析"><a href="#4-趋势分析" class="headerlink" title="4.趋势分析"></a>4.趋势分析</h2><h3 id="1-特征分类（分面检索）"><a href="#1-特征分类（分面检索）" class="headerlink" title="(1)特征分类（分面检索）"></a>(1)特征分类（分面检索）</h3><blockquote>
<p>您希望在零售网站上提供<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Faceted_search">分面检索</a>功能，使客户能够在网页中列出的产品结果中，通过选择指定特征来改善他们的产品搜索。</p>
<p>按不同维度对产品进行分类是有益的，其中每个维度（即「分面facet」）对应于产品记录中的特定字段（例如「产品评级」、「产品价格」）。每个分面都应分解为子范围，以便客户可以为特定分面（例如「评级」）选择特定的子范围（例如4~5星）。</p>
<p>聚合管道将根据每个分面的字段（<code>rating</code>和<code>price</code>）来分析<code>products</code>集合，以确定每个分面的值的分布。</p>
<p>产品集合<code>products</code>包含如下16个文档。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Asus Laptop&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;ELECTRONICS&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Good value laptop for students&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;431.43&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.2&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;The Day Of The Triffids&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;BOOKS&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Classic post-apocalyptic novel&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;5.01&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.8&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Morphy Richardds Food Mixer&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;KITCHENWARE&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Luxury mixer turning good cakes into great&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;63.13&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;3.8&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Karcher Hose Set&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;GARDEN&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Hose + nosels + winder for tidy storage&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;22.13&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.3&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Oak Coffee Table&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;HOME&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;size is 2m x 0.5m x 0.4m&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;22.13&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;3.8&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Lenovo Laptop&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;ELECTRONICS&quot;</span>,</span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;High spec good for gaming&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;1299.99&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.1&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;One Day in the Life of Ivan Denisovich&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;BOOKS&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Brutal life in a labour camp&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.29&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.9&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Russell Hobbs Chrome Kettle&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;KITCHENWARE&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Nice looking budget kettle&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;15.76&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;3.9&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Tiffany Gold Chain&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;JEWELERY&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Looks great for any age and gender&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;582.22&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.0&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Raleigh Racer 21st Century Classic&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;BICYCLES&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Modern update to a classic 70s bike design&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;523.00&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.5&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Diesel Flare Jeans&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;CLOTHES&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Top end casual look&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;129.89&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.3&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Jazz Silk Scarf&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;CLOTHES&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Style for the winder months&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;28.39&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;3.7&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Dell XPS 13 Laptop&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;ELECTRONICS&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Developer edition&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;1399.89&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.4&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;NY Baseball Cap&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;CLOTHES&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Blue &amp; white&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;18.99&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.0&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Tots Flower Pots&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;GARDEN&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Set of three&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;9.78&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.1&quot;</span>)</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Picky Pencil Sharpener&quot;</span>, <span class="string">&quot;category&quot;</span>: <span class="string">&quot;Stationery&quot;</span>, </span><br><span class="line">    <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Ultra budget&quot;</span>, </span><br><span class="line">    <span class="string">&quot;price&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;0.67&quot;</span>), <span class="string">&quot;rating&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;1.2&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  <span class="comment">// Group products by 2 facets: 1) by price ranges, 2) by rating ranges</span></span><br><span class="line">  &#123;<span class="string">&quot;$facet&quot;</span>: &#123;</span><br><span class="line">    <span class="comment">// ①Group by price ranges</span></span><br><span class="line">    <span class="string">&quot;by_price&quot;</span>: [</span><br><span class="line">      <span class="comment">// Group into 3 ranges: inexpensive small price range to expensive large price range</span></span><br><span class="line">      &#123;<span class="string">&quot;$bucketAuto&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;groupBy&quot;</span>: <span class="string">&quot;$price&quot;</span>,</span><br><span class="line">        <span class="string">&quot;buckets&quot;</span>: <span class="number">3</span>,</span><br><span class="line">        <span class="string">&quot;granularity&quot;</span>: <span class="string">&quot;1-2-5&quot;</span>,</span><br><span class="line">        <span class="string">&quot;output&quot;</span>: &#123;<span class="string">&quot;count&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="number">1</span>&#125;, <span class="string">&quot;products&quot;</span>: &#123;<span class="string">&quot;$push&quot;</span>: <span class="string">&quot;$name&quot;</span>&#125;,&#125;,</span><br><span class="line">      &#125;&#125;,</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// Tag range info as &quot;price_range&quot;</span></span><br><span class="line">      &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;price_range&quot;</span>: <span class="string">&quot;$_id&quot;</span>,&#125;&#125;,         </span><br><span class="line">      </span><br><span class="line">      &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>,]&#125;,         </span><br><span class="line">    ],</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ②Group by rating ranges</span></span><br><span class="line">    <span class="string">&quot;by_rating&quot;</span>: [</span><br><span class="line">      <span class="comment">// Group products evenly across 5 rating ranges from low to high</span></span><br><span class="line">      &#123;<span class="string">&quot;$bucketAuto&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;groupBy&quot;</span>: <span class="string">&quot;$rating&quot;</span>,</span><br><span class="line">        <span class="string">&quot;buckets&quot;</span>: <span class="number">5</span>,</span><br><span class="line">        <span class="string">&quot;output&quot;</span>: &#123;<span class="string">&quot;count&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="number">1</span>&#125;, <span class="string">&quot;products&quot;</span>: &#123;<span class="string">&quot;$push&quot;</span>: <span class="string">&quot;$name&quot;</span>&#125;,&#125;,</span><br><span class="line">      &#125;&#125;,</span><br><span class="line">      </span><br><span class="line">      &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;rating_range&quot;</span>: <span class="string">&quot;$_id&quot;</span>,&#125;&#125;,         </span><br><span class="line">      &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>,]&#125;,         </span><br><span class="line">    ],</span><br><span class="line">  &#125;&#125;,  </span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后应该返回一个单独的文档，文档包含 2 个分面（分别关闭<code>by_price</code>和关闭<code>by_rating</code>），每个分面显示「其值的子范围」以及「属于每个子范围的产品」，如下所示。<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210928-9特征分类之分面检索.png" alt="特征分类之分面检索"></p>
<ul>
<li><p><strong>Multiple Pipelines.</strong> The <code>$facet</code> stage doesn’t have to be employed for you to use the <code>$bucketAuto</code> stage. In most <em>faceted search</em> scenarios, you will want to understand a collection by multiple dimensions at once (<em>price</em> &amp; <em>rating</em> in this case). The <code>$facet</code> stage is convenient because it allows you to define various <code>$bucketAuto</code> dimensions in one go in a single pipeline. Otherwise, a client application must invoke an aggregation multiple times, each using a new <code>$bucketAuto</code> stage to process a different field. In fact, each section of a <code>$facet</code> stage is just a regular aggregation [sub-]pipeline, able to contain any type of stage (with a few specific <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/facet/#behavior">documented exceptions</a>) and may not even contain <code>$bucketAuto</code> or <code>$bucket</code> stages at all. </p>
</li>
<li><p><strong>Single Document Result.</strong> If the result of a <code>$facet</code> based aggregation is allowed to be multiple documents, this will cause a problem. The results will contain a mix of records originating from different facets but with no way of ascertaining the facet each result record belongs to. Consequently, when using <code>$facet</code>, a single document is always returned, containing top-level fields identifying each facet. Having only a single result record is not usually a problem. A typical requirement for faceted search is to return a small amount of grouped summary data about a collection rather than large amounts of raw data from the collection. Therefore the 16MB document size limit should not be an issue.</p>
</li>
<li><p><strong>Spread Of Ranges.</strong> In this example, each of the two employed bucketing facets uses a different granularity number scheme for spreading out the sub-ranges of values. You choose a numbering scheme based on what you know about the nature of the facet. For instance, most of the <em>ratings</em> values in the sample collection have scores bunched between late 3s and early 4s. If a numbering scheme is defined to reflect an even spread of ratings, most products will appear in the same sub-range bucket and some sub-ranges would contain no products (e.g. ratings 2 to 3 in this example). This wouldn’t provide website customers with much selectivity on product ratings.</p>
</li>
</ul>
<h3 id="2-最大图网络"><a href="#2-最大图网络" class="headerlink" title="(2)最大图网络"></a>(2)最大图网络</h3><blockquote>
<p>基于类似于 <em>Twitter</em> 的社交网络数据库，您的组织希望了解新营销活动的最佳目标。</p>
<p>您想要搜索「社交网络用户」的集合，其中每个用户记录文档都包含一个<strong>用户名</strong>和该用户的<strong>关注者</strong>。</p>
<p>您将执行一个聚合管道查询，遍历每个用户记录的<code>followed_by</code>数组，从而确定哪个用户具有最大的 <em>network reach</em> ，即最广的<strong>朋友圈</strong>。</p>
<p>请注意，为简洁起见，此示例仅使用了一个简单的数据模型。然而这不太可能是一个最佳数据模型，例如拥有很多粉丝的社交网络用户对于<code>$graphLookup</code>的大规模使用，抑或是在分片环境中运行。有关此类问题的更多指导，请参阅<a target="_blank" rel="noopener" href="https://github.com/mongodb-labs/socialite">Socialite</a>。</p>
<p>用户集合<code>users</code>包含以下10个社交网络用户文档，可考虑加上一个索引<code>db.users.createIndex(&#123;&quot;name&quot;: 1&#125;)</code>来帮助优化 <strong>图的遍历</strong> 。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Paul&quot;</span>,  <span class="string">&quot;followed_by&quot;</span>: [] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Toni&quot;</span>,  <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Paul&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Janet&quot;</span>, <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Paul&quot;</span>, <span class="string">&quot;Toni&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;David&quot;</span>, <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Janet&quot;</span>, <span class="string">&quot;Paul&quot;</span>, <span class="string">&quot;Toni&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Fiona&quot;</span>, <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;David&quot;</span>, <span class="string">&quot;Paul&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>,   <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Janet&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Carl&quot;</span>,  <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Fiona&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Sarah&quot;</span>, <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Carl&quot;</span>, <span class="string">&quot;Paul&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Carol&quot;</span>, <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Helen&quot;</span>, <span class="string">&quot;Sarah&quot;</span>] &#125;,</span><br><span class="line">&#123; <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Helen&quot;</span>, <span class="string">&quot;followed_by&quot;</span>: [<span class="string">&quot;Paul&quot;</span>] &#125;,</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  <span class="comment">// 对于每个用户，图遍历他们的followed_by数组</span></span><br><span class="line">  &#123;<span class="string">&quot;$graphLookup&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;from&quot;</span>: <span class="string">&quot;users&quot;</span>,</span><br><span class="line">    <span class="string">&quot;startWith&quot;</span>: <span class="string">&quot;$followed_by&quot;</span>,</span><br><span class="line">    <span class="string">&quot;connectFromField&quot;</span>: <span class="string">&quot;followed_by&quot;</span>,</span><br><span class="line">    <span class="string">&quot;connectToField&quot;</span>: <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="string">&quot;depthField&quot;</span>: <span class="string">&quot;depth&quot;</span>,</span><br><span class="line">    <span class="string">&quot;as&quot;</span>: <span class="string">&quot;extended_network&quot;</span>,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line"></span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;</span><br><span class="line">    <span class="comment">// Count the extended connection reach</span></span><br><span class="line">    <span class="string">&quot;network_reach&quot;</span>: &#123;<span class="string">&quot;$size&quot;</span>: <span class="string">&quot;$extended_network&quot;</span>&#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Gather the list of the extended connections&#x27; names</span></span><br><span class="line">    <span class="string">&quot;extended_connections&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;$map&quot;</span>: &#123;<span class="string">&quot;input&quot;</span>: <span class="string">&quot;$extended_network&quot;</span>, <span class="string">&quot;as&quot;</span>: <span class="string">&quot;connection&quot;</span>,<span class="string">&quot;in&quot;</span>: <span class="string">&quot;$$connection.name&quot;</span>&#125;</span><br><span class="line">    &#125;,    </span><br><span class="line">  &#125;&#125;,</span><br><span class="line">    </span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, <span class="string">&quot;followed_by&quot;</span>, <span class="string">&quot;extended_network&quot;</span>,]&#125;,   </span><br><span class="line">  &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;network_reach&quot;</span>: -<span class="number">1</span>,&#125;&#125;,   </span><br><span class="line">];</span><br></pre></td></tr></table></figure></p>
<p>执行聚合操作后应该返回对应原来的10个源社交网络用户的10个文档，每个文档包括用户的 <em>网络到达数<code>network reach count</code></em> 和 <em>扩展连接名称<code>extended connection names</code></em> ，按网络覆盖面最广的用户排序，如下所示。</p>
<p><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-1最大图网络.png" alt="最大图网络"></p>
<ul>
<li><p><strong>Following Graphs.</strong> The <code>$graphLookup</code> stage helps you traverse relationships between records, looking for patterns that aren’t necessarily evident from looking at each record in isolation. In this example, by looking at <em>Paul’s</em> record in isolation, it is evident that <em>Paul</em> has no <em>friends</em> and thus has the lowest network reach. However, it is not obvious that <em>Carol</em> has the greatest network reach just by looking at the number of people <em>Carol</em> is directly followed by, which is two. <em>David</em>, for example, is followed by three people (one more than <em>Carol</em>). However, the executed aggregation pipeline can deduce that <em>Carol</em> has the most extensive network reach.</p>
</li>
<li><p><strong>Index Use.</strong> The <code>$graphLookup</code> stage can leverage the index on the field <code>name</code> for each of its <code>connectToField</code> hops.</p>
</li>
</ul>
<h3 id="3-增量分析"><a href="#3-增量分析" class="headerlink" title="(3)增量分析"></a>(3)增量分析</h3><blockquote>
<p>您有一组累积多年的 <em>商店订单</em> ，零售渠道在每个交易日不断向<code>orders</code>集合添加新订单记录。</p>
<p>您希望经常生成汇总报告，以便管理层了解业务状态并对不断变化的业务趋势做出反应。多年来，生成所有每日总和和平均值的报告所需的时间越来越长，因为需要处理的数据天数越来越多。</p>
<p>从现在开始，为了解决这个问题，您将只在一天结束时生成当天的摘要分析，并将其存储在不同的集合中，该集合会随着时间的推移积累每日摘要记录。</p>
<p>与本文中的大多数示例不同，聚合管道将其输出写入一个集合，而不是将结果以流的形式传输回调用应用程序。</p>
<p>订单集合<code>orders</code>包含9个文档，分别代表 2021-02-01 的 5 个订单和 2021-02-02 的 4 个订单，如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-01T08:35:52Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;231.43&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-01T09:32:07Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;99.99&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-01T08:25:37Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;63.13&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-01T19:13:32Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;2.01&quot;</span>),&#125;,  </span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-01T22:56:53Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;187.99&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-02T23:04:48Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;4.59&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-02T08:55:46Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;48.50&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-02T07:49:32Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;1024.89&quot;</span>),&#125;,</span><br><span class="line">&#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-02T13:49:44Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;102.24&quot;</span>),&#125;,</span><br></pre></td></tr></table></figure>
<p>定义一个函数来创建聚合管道，将「开始日期」和「结束日期」作为函数的参数，以便于在不同的日期多次执行聚合：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getDayAggPipeline</span>(<span class="params">startDay, endDay</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> [</span><br><span class="line">    &#123;<span class="string">&quot;$match&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;orderdate&quot;</span>: &#123;<span class="string">&quot;$gte&quot;</span>: <span class="title class_">ISODate</span>(startDay), <span class="string">&quot;$lt&quot;</span>: <span class="title class_">ISODate</span>(endDay)&#125;</span><br><span class="line">    &#125;&#125;,</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 将当天的所有订单组合成一个汇总记录</span></span><br><span class="line">    &#123;<span class="string">&quot;$group&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;_id&quot;</span>: <span class="literal">null</span>,</span><br><span class="line">      <span class="string">&quot;date_parts&quot;</span>: &#123;<span class="string">&quot;$first&quot;</span>: &#123;<span class="string">&quot;$dateToParts&quot;</span>: &#123;<span class="string">&quot;date&quot;</span>: <span class="string">&quot;$orderdate&quot;</span>&#125;&#125;&#125;,</span><br><span class="line">      <span class="string">&quot;total_value&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="string">&quot;$value&quot;</span>&#125;,</span><br><span class="line">      <span class="string">&quot;total_orders&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="number">1</span>&#125;,</span><br><span class="line">    &#125;&#125;,</span><br><span class="line">      </span><br><span class="line">    <span class="comment">// 从1个订单中获取日期部分（对于UTC而言需要年、月、日）</span></span><br><span class="line">    &#123;<span class="string">&quot;$set&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;day&quot;</span>: &#123;<span class="string">&quot;$dateFromParts&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;year&quot;</span>: <span class="string">&quot;$date_parts.year&quot;</span>, </span><br><span class="line">          <span class="string">&quot;month&quot;</span>: <span class="string">&quot;$date_parts.month&quot;</span>,</span><br><span class="line">          <span class="string">&quot;day&quot;</span>:<span class="string">&quot;$date_parts.day&quot;</span></span><br><span class="line">       &#125;&#125;,</span><br><span class="line">    &#125;&#125;,</span><br><span class="line">        </span><br><span class="line">    &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, <span class="string">&quot;date_parts&quot;</span>, ]&#125;,</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 将日期摘要添加到摘要集合中（若已存在则覆盖）</span></span><br><span class="line">    &#123;<span class="string">&quot;$merge&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;into&quot;</span>: <span class="string">&quot;daily_orders_summary&quot;</span>, <span class="string">&quot;on&quot;</span>: <span class="string">&quot;day&quot;</span>,</span><br><span class="line">      <span class="string">&quot;whenMatched&quot;</span>: <span class="string">&quot;replace&quot;</span>, <span class="string">&quot;whenNotMatched&quot;</span>: <span class="string">&quot;insert&quot;</span></span><br><span class="line">    &#125;&#125;,   </span><br><span class="line">  ];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>仅对于 2021-02-01 的订单，调用上述<code>getDayAggPipeline</code>函数构建聚合管道，执行聚合后将结果写入汇总集合<code>daily_orders_summary</code>，查看汇总集合内容应该只有1条记录【仅生成了 2021-02-01 的单个订单摘要，其中包含当天的总价值和订单数量】。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 【第一天】</span></span><br><span class="line"><span class="keyword">var</span> pipeline = <span class="title function_">getDayAggPipeline</span>(<span class="string">&quot;2021-02-01T00:00:00Z&quot;</span>, <span class="string">&quot;2021-02-02T00:00:00Z&quot;</span>);</span><br><span class="line">db.<span class="property">orders</span>.<span class="title function_">aggregate</span>(pipeline);</span><br><span class="line">db.<span class="property">daily_orders_summary</span>.<span class="title function_">find</span>()</span><br></pre></td></tr></table></figure>
<p>现在仅在第二天（即仅对于 2021-02-02 的订单），构建管道并执行聚合。从结果中，您可以看到这两天的订单摘要都存在（在第一天的基础上追加了第二天）。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 【第二天】</span></span><br><span class="line"><span class="keyword">var</span> pipeline = <span class="title function_">getDayAggPipeline</span>(<span class="string">&quot;2021-02-02T00:00:00Z&quot;</span>, <span class="string">&quot;2021-02-03T00:00:00Z&quot;</span>);</span><br><span class="line">db.<span class="property">orders</span>.<span class="title function_">aggregate</span>(pipeline);</span><br><span class="line">db.<span class="property">daily_orders_summary</span>.<span class="title function_">find</span>()</span><br></pre></td></tr></table></figure>
<p>为了模拟组织偶尔需要追溯更正旧订单，回到第一天并添加新的「高价值」订单。然后仅在第一天（2021-02-01）重新运行聚合，以表明您可以安全正确地重新计算 <strong>仅一天</strong> 的摘要：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 回顾性地将订单添加到第一天（2021-02-01）以模拟「漏掉一单」</span></span><br><span class="line">db.<span class="property">orders</span>.<span class="title function_">insertOne</span>(</span><br><span class="line">  &#123;<span class="string">&quot;orderdate&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-02-01T09:32:07Z&quot;</span>), <span class="string">&quot;value&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;11111.11&quot;</span>)&#125;</span><br><span class="line">  )</span><br><span class="line"></span><br><span class="line"><span class="comment">// 【第一天（新）】重新为第一天构建运行聚合管道，覆盖汇总集合daily_orders_summary中的第一条记录</span></span><br><span class="line"><span class="keyword">var</span> pipeline = <span class="title function_">getDayAggPipeline</span>(<span class="string">&quot;2021-02-01T00:00:00Z&quot;</span>, <span class="string">&quot;2021-02-02T00:00:00Z&quot;</span>);</span><br><span class="line">db.<span class="property">orders</span>.<span class="title function_">aggregate</span>(pipeline);</span><br><span class="line">db.<span class="property">daily_orders_summary</span>.<span class="title function_">find</span>()</span><br></pre></td></tr></table></figure>
<p>执行上述三个聚合操作后，汇总集合<code>daily_orders_summary</code>如图所示：<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-2增量分析.png" alt="增量分析"></p>
<p>从结果中，您可以看到仍然存在两个订单摘要，两个交易日各一个，但第一天的总价值和订单数量发生了变化。如下图所示，当需要更正某天的订单摘要时，只要重新为那一天构建运行聚合管道即可修正。</p>
<p><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210818模拟漏单情况.png" alt="模拟漏单情况修正对比"></p>
<ul>
<li><p><strong>Merging Results.</strong> The pipeline uses a <code>$merge</code> stage to instruct the aggregation engine to write the output to a collection rather than returning a stream of results. In this example, with the options you provide to <code>$merge</code>, the aggregation inserts a new record in the destination collection if a matching one doesn’t already exist. If a matching record already exists, it replaces the previous version.</p>
</li>
<li><p><strong>Incremental Updates.</strong> The example illustrates just two days of shop orders, albeit with only a few orders, to keep the example simple. At the end of each new trading day, you run the aggregation pipeline to generate the current day’s summary only. Even after the source collection has increased in size over many years, the time it takes you to bring the summary collection up to date again stays constant. In a real-world scenario, the business might expose a graphical chart showing the changing daily orders trend over the last rolling year. This charting dashboard is not burdened by the cost of periodically regenerating values for all days in the year. There could be hundreds of thousands of orders received per day for real-world retailers, especially large ones. A day’s summary may take many seconds to generate in that situation. Without an <em>incremental analytics</em> approach, if you need to generate a year’s worth of daily summaries every time, it would take hours to refresh the business dashboard.</p>
<ul>
<li><p><strong>Idempotency.</strong> If a retailer is aggregating tens of thousands of orders per day, then during end-of-day processing, it may choose to generate 24 hourly summary records rather than a single daily record.  This provides the business with finer granularity to understand trends better. As with any software process, when generating hourly results into the summary collection, there is the risk of not fully completing if a system failure occurs. If an in-flight aggregation terminates abnormally, it may not have written all 24 summary collection records. The failure leaves the summary collection in an indeterminate and incomplete state for one of its days. However, this isn’t a problem because of the way the aggregation pipeline uses the <code>$merge</code> stage. When an aggregation fails to complete, it can just be re-run. When re-run, it will regenerate all the results for the day, replacing existing summary records and filling in the missing ones. The aggregation pipeline is idempotent, and you can run it repeatedly without damaging the summary collection. The overall solution is self-healing and naturally tolerant of inadvertently aborted aggregation jobs.</p>
</li>
<li><p><strong>Retrospective Changes.</strong> Sometimes, an organisation may need to go back and correct records from the past, as illustrated in this example. For instance, a bank may need to fix a past payment record due to a settlement issue that only comes to light weeks later. With the approach used in this example, it is straightforward to re-execute the aggregation pipeline for a prior date, using the updated historical data. This will correctly update the specific day’s summary data only, to reflect the business’s current state.</p>
</li>
</ul>
</li>
</ul>
<h2 id="5-数据安全"><a href="#5-数据安全" class="headerlink" title="5.数据安全"></a>5.数据安全</h2><h3 id="1-严格视图"><a href="#1-严格视图" class="headerlink" title="(1)严格视图"></a>(1)严格视图</h3><blockquote>
<p>您有一个 <code>persons</code> 集合，其中不应允许特定客户端应用程序查看敏感信息。因此，您将仅提供一个筛选后的人员数据子集的只读视图。</p>
<p>在实际情况中，您还可以使用 MongoDB 的<strong>基于角色的访问控制（Role-Based Access Control，RBAC）</strong>来限制客户端应用程序只能访问视图而不是原始集合。</p>
<p>您将使用 <code>adults</code> 视图以两种方式限制客户端应用程序的个人数据：</p>
<ol>
<li>仅显示 18 岁及以上的人（通过检查每个人的<code>dateofbirth</code>字段）；</li>
<li>从结果中排除每个人的<code>social_security_num</code>字段。</li>
</ol>
<p>本质上，这是对 MongoDB 中实现「记录级（record-level）」访问控制的一个说明。</p>
<p><code>persons</code>集合包含5条记录，如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;6392529400&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Elise&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Smith&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1972-01-13T09:32:07Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;email&quot;</span>: <span class="string">&quot;elise_smith@myemail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;social_security_num&quot;</span>: <span class="string">&quot;507-28-9805&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123;<span class="string">&quot;number&quot;</span>: <span class="number">5625</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Tipa Circle&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Wojzinmoj&quot;</span>&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;1723338115&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Olive&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Ranieri&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1985-05-12T23:14:30Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;email&quot;</span>: <span class="string">&quot;oranieri@warmmail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;social_security_num&quot;</span>: <span class="string">&quot;618-71-2912&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123;<span class="string">&quot;number&quot;</span>: <span class="number">9303</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Mele Circle&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Tobihbo&quot;</span>&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;8732762874&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Toni&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Jones&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2014-11-23T16:53:56Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;email&quot;</span>: <span class="string">&quot;tj@wheresmyemail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;social_security_num&quot;</span>: <span class="string">&quot;001-10-3488&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123;<span class="string">&quot;number&quot;</span>: <span class="number">1</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;High Street&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Upper Abbeywoodington&quot;</span>&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;7363629563&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Bert&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Gooding&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;1941-04-07T22:11:52Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;gender&quot;</span>: <span class="string">&quot;MALE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;email&quot;</span>: <span class="string">&quot;bgooding@tepidmail.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;social_security_num&quot;</span>: <span class="string">&quot;230-43-7633&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123;<span class="string">&quot;number&quot;</span>: <span class="number">13</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Upper Bold Road&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Redringtonville&quot;</span>&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;person_id&quot;</span>: <span class="string">&quot;1029648329&quot;</span>,</span><br><span class="line">    <span class="string">&quot;firstname&quot;</span>: <span class="string">&quot;Sophie&quot;</span>, <span class="string">&quot;lastname&quot;</span>: <span class="string">&quot;Celements&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dateofbirth&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2013-07-06T17:35:45Z&quot;</span>),    </span><br><span class="line">    <span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>,</span><br><span class="line">    <span class="string">&quot;email&quot;</span>: <span class="string">&quot;sophe@celements.net&quot;</span>,</span><br><span class="line">    <span class="string">&quot;social_security_num&quot;</span>: <span class="string">&quot;377-30-5364&quot;</span>,</span><br><span class="line">    <span class="string">&quot;address&quot;</span>: &#123;<span class="string">&quot;number&quot;</span>: <span class="number">5</span>, <span class="string">&quot;street&quot;</span>: <span class="string">&quot;Innings Close&quot;</span>, <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Basilbridge&quot;</span>&#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  &#123;<span class="string">&quot;$match&quot;</span>: &#123;<span class="string">&quot;$expr&quot;</span>:&#123;</span><br><span class="line">      <span class="string">&quot;$lt&quot;</span>: [<span class="string">&quot;$dateofbirth&quot;</span>, &#123;<span class="string">&quot;$subtract&quot;</span>: [<span class="string">&quot;$$NOW&quot;</span>, <span class="number">18</span>*<span class="number">365.25</span>*<span class="number">24</span>*<span class="number">60</span>*<span class="number">60</span>*<span class="number">1000</span>]&#125;]</span><br><span class="line">  &#125;&#125;&#125;,</span><br><span class="line">  &#123;<span class="string">&quot;$unset&quot;</span>: [<span class="string">&quot;_id&quot;</span>, <span class="string">&quot;social_security_num&quot;</span>]&#125;,    </span><br><span class="line">];</span><br></pre></td></tr></table></figure></p>
<p>首先，在创建视图之前执行聚合操作（并观察explain），以测试该定义的聚合管道。然后创建新的<code>adults</code>视图，它会在任何人查询视图时自动应用聚合管道。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">db.<span class="property">persons</span>.<span class="title function_">aggregate</span>(pipeline);</span><br><span class="line">db.<span class="property">persons</span>.<span class="title function_">explain</span>(<span class="string">&quot;executionStats&quot;</span>).<span class="title function_">aggregate</span>(pipeline);</span><br><span class="line">db.<span class="title function_">createView</span>(<span class="string">&quot;adults&quot;</span>, <span class="string">&quot;persons&quot;</span>, pipeline);</span><br></pre></td></tr></table></figure>
<p>对创建的视图执行常规 MQL 查询，没有任何过滤条件，并观察其explain；或者创建的视图执行 MQL 查询，指定过滤器为仅返回女性成年人，观察explain注意性别过滤器<code>gender</code>是如何影响它的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 常规MQL查询</span></span><br><span class="line">db.<span class="property">adults</span>.<span class="title function_">find</span>();</span><br><span class="line">db.<span class="property">adults</span>.<span class="title function_">explain</span>(<span class="string">&quot;executionStats&quot;</span>).<span class="title function_">find</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 带性别过滤器的MQL查询</span></span><br><span class="line">db.<span class="property">adults</span>.<span class="title function_">find</span>(&#123;<span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>&#125;);</span><br><span class="line">db.<span class="property">adults</span>.<span class="title function_">explain</span>(<span class="string">&quot;executionStats&quot;</span>).<span class="title function_">find</span>(&#123;<span class="string">&quot;gender&quot;</span>: <span class="string">&quot;FEMALE&quot;</span>&#125;);</span><br></pre></td></tr></table></figure>
<p>对于<code>aggregate()</code>和<code>find()</code>命令在 <em>视图</em> 上执行的结果应该是一样的，都返回3个文档，表示超过18岁的3个人，但是没有显示他们实际的出生日期，如图所示：</p>
<p><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-3严格视图1.png" alt="严格视图之常规MQL查询和聚合查询"></p>
<p>带有<code>&quot;gender&quot;: &quot;FEMALE&quot;</code>过滤器的<code>find()</code>命令在 <em>视图</em> 上运行的结果应该仅有2条 <strong>女性</strong> 记录，因为男性记录已被排除，如下所示：</p>
<p><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-3严格视图2.png" alt="严格视图之带过滤器的MQL查询"></p>
<ul>
<li><p><strong>Expr &amp; Indexes.</strong> The <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/aggregation-variables/">“NOW” system variable</a> used here returns the current system date-time. However, you can only access this system variable via an <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#expressions">aggregation expression</a> and not directly via the regular MongoDB query syntax used by MQL and <code>$match</code>. You must wrap an expression using <code>$$NOW</code> inside an <code>$expr</code> operator. As described in the section <em><a href="../../guides/expressions.md#expression-restrictions">Restrictions When Using Expressions</a></em> in an earlier chapter, if you use an <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/query/expr/">$expr query operator</a> to perform a range comparison, you can’t make use of an index in versions of MongoDB earlier then 5.0. Therefore, in this example, unless you use MongoDB 5.0, the aggregation will not take advantage of an index on <code>dateofbirth</code>. For a view, because you specify the pipeline earlier than it is ever run, you cannot obtain the current date-time at runtime by other means.</p>
</li>
<li><p><strong>View Finds &amp; Indexes.</strong> Even for versions of MongoDB before 5.0, the explain plan for the <em>gender query</em> run against the view shows an index has been used (the index defined for the <code>gender</code> field). At runtime, a view is essentially just an aggregation pipeline defined “ahead of time”. When <code>db.adults.find(&#123;&quot;gender&quot;: &quot;FEMALE&quot;&#125;)</code> is executed, the database engine dynamically appends a new <code>$match</code> stage to the end of the pipeline for the gender match. It then optimises the pipeline by moving the new <code>$match</code> stage to the pipeline’s start. Finally, it adds the filter extracted from the new <code>$match</code> stage to the aggregation’s initial query, and hence it can then leverage an index containing the <code>gender</code> field. The following two excerpts, from an explain plan from a MongoDB version before 5.0, illustrate how the filter on <code>gender</code> and the filter on <code>dateofbirth</code> combine at runtime and how the index for <code>gender</code> is used to avoid a full collection scan:</p>
 <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;$cursor&#x27;</span>: &#123;</span><br><span class="line">  <span class="attr">queryPlanner</span>: &#123;</span><br><span class="line">    <span class="attr">plannerVersion</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="attr">namespace</span>: <span class="string">&#x27;book-restricted-view.persons&#x27;</span>,</span><br><span class="line">    <span class="attr">indexFilterSet</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">parsedQuery</span>: &#123;</span><br><span class="line">      <span class="string">&#x27;$and&#x27;</span>: [</span><br><span class="line">        &#123; <span class="attr">gender</span>: &#123; <span class="string">&#x27;$eq&#x27;</span>: <span class="string">&#x27;FEMALE&#x27;</span> &#125; &#125;,</span><br><span class="line">        &#123; <span class="string">&#x27;$expr&#x27;</span>: &#123; <span class="string">&#x27;$lt&#x27;</span>: [ <span class="string">&#x27;$dateofbirth&#x27;</span>,</span><br><span class="line">          &#123;</span><br><span class="line">             <span class="string">&#x27;$subtract&#x27;</span>: [ <span class="string">&#x27;$$NOW&#x27;</span>, &#123; <span class="string">&#x27;$const&#x27;</span>: <span class="number">568036800000</span> &#125; ]</span><br><span class="line">             ...</span><br></pre></td></tr></table></figure>
 <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inputStage</span>: &#123;</span><br><span class="line">  <span class="attr">stage</span>: <span class="string">&#x27;IXSCAN&#x27;</span>,</span><br><span class="line">  <span class="attr">keyPattern</span>: &#123; <span class="attr">gender</span>: <span class="number">1</span> &#125;,</span><br><span class="line">  <span class="attr">indexName</span>: <span class="string">&#x27;gender_1&#x27;</span>,</span><br><span class="line">  <span class="attr">direction</span>: <span class="string">&#x27;forward&#x27;</span>,</span><br><span class="line">  <span class="attr">indexBounds</span>: &#123; <span class="attr">gender</span>: [ <span class="string">&#x27;[&quot;FEMALE&quot;, &quot;FEMALE&quot;]&#x27;</span> ] &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p> In MongoDB 5.0, the explain plan will show the aggregation runtime executing the pipeline more optimally by entirely using the compound index based on both the fields <code>gender</code> and <code>dateofbirth</code>.</p>
</li>
<li><p><strong>Further Reading.</strong> The ability for <em>find</em> operations on a view to automatically push filters into the view’s aggregation pipeline, and then be further optimised, is described in the blog post: <a target="_blank" rel="noopener" href="https://pauldone.blogspot.com/2020/11/mongdb-views-optimisations.html">Is Querying A MongoDB View Optimised?</a></p>
</li>
</ul>
<h3 id="2-隐藏敏感字段"><a href="#2-隐藏敏感字段" class="headerlink" title="(2)隐藏敏感字段"></a>(2)隐藏敏感字段</h3><p>由于使用<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/rand/">$rand</a>运算符，要求MongoDB版本最低为 4.4。</p>
<blockquote>
<p>您想对 <em>信用卡付款</em> 集合的敏感字段执行不可逆屏蔽，准备将输出数据集提供给第 3 方进行分析，而不会将敏感信息暴露给第 3 方。</p>
<p>您需要对付款字段进行的具体更改是：</p>
<ul>
<li>部分混淆持卡人姓名；</li>
<li>混淆卡号的前 12 位数字，只保留最后 4 位数字；</li>
<li>通过添加或减去最多 30 天（约 1 个月）的随机金额来调整卡的到期时间；</li>
<li>用一组随机的 3 位数字替换卡的 3 位安全码；</li>
<li>通过添加或减去最多为原始金额 10% 的随机金额来调整交易金额；</li>
<li>将大约 20% 的记录中<code>reported</code>字段的布尔值更改为相反值；</li>
<li>如果嵌套子文档<code>customer_info</code>的<code>category</code>字段为 <em><code>RESTRICTED</code></em> ，则排除整个子文档<code>customer_info</code>。</li>
</ul>
<p><code>payments</code>集合包含2条信用卡付款记录的文档，其中包含敏感数据，如下所示。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;card_name&quot;</span>: <span class="string">&quot;Mrs. Jane A. Doe&quot;</span>,</span><br><span class="line">    <span class="string">&quot;card_num&quot;</span>: <span class="string">&quot;1234567890123456&quot;</span>,</span><br><span class="line">    <span class="string">&quot;card_expiry&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2023-08-31T23:59:59Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;card_sec_code&quot;</span>: <span class="string">&quot;123&quot;</span>,</span><br><span class="line">    <span class="string">&quot;card_type&quot;</span>: <span class="string">&quot;CREDIT&quot;</span>,        </span><br><span class="line">    <span class="string">&quot;transaction_id&quot;</span>: <span class="string">&quot;eb1bd77836e8713656d9bf2debba8900&quot;</span>,</span><br><span class="line">    <span class="string">&quot;transaction_date&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-01-13T09:32:07Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;transaction_amount&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;501.98&quot;</span>),</span><br><span class="line">    <span class="string">&quot;reported&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="string">&quot;customer_info&quot;</span>: &#123;<span class="string">&quot;category&quot;</span>: <span class="string">&quot;RESTRICTED&quot;</span>, <span class="string">&quot;rating&quot;</span>: <span class="number">89</span>, <span class="string">&quot;risk&quot;</span>: <span class="number">3</span>,&#125;,</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;card_name&quot;</span>: <span class="string">&quot;Jim Smith&quot;</span>,</span><br><span class="line">    <span class="string">&quot;card_num&quot;</span>: <span class="string">&quot;9876543210987654&quot;</span>,</span><br><span class="line">    <span class="string">&quot;card_expiry&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2022-12-31T23:59:59Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;card_sec_code&quot;</span>: <span class="string">&quot;987&quot;</span>,</span><br><span class="line">    <span class="string">&quot;card_type&quot;</span>: <span class="string">&quot;DEBIT&quot;</span>,        </span><br><span class="line">    <span class="string">&quot;transaction_id&quot;</span>: <span class="string">&quot;634c416a6fbcf060bb0ba90c4ad94f60&quot;</span>,</span><br><span class="line">    <span class="string">&quot;transaction_date&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2020-11-24T19:25:57Z&quot;</span>),</span><br><span class="line">    <span class="string">&quot;transaction_amount&quot;</span>: <span class="title class_">NumberDecimal</span>(<span class="string">&quot;64.01&quot;</span>),</span><br><span class="line">    <span class="string">&quot;reported&quot;</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&quot;customer_info&quot;</span>: &#123;<span class="string">&quot;category&quot;</span>: <span class="string">&quot;NORMAL&quot;</span>, <span class="string">&quot;rating&quot;</span>: <span class="number">78</span>, <span class="string">&quot;risk&quot;</span>: <span class="number">55</span>,&#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>聚合管道定义如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipeline = [</span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;card_name&quot;</span>: &#123;<span class="string">&quot;$regexFind&quot;</span>: &#123;<span class="string">&quot;input&quot;</span>: <span class="string">&quot;$card_name&quot;</span>, <span class="string">&quot;regex&quot;</span>: <span class="regexp">/(\S+)$/</span>&#125;&#125;,</span><br><span class="line">    <span class="string">&quot;card_num&quot;</span>: &#123;<span class="string">&quot;$concat&quot;</span>: [<span class="string">&quot;XXXXXXXXXXXX&quot;</span>, &#123;<span class="string">&quot;$substrCP&quot;</span>: [<span class="string">&quot;$card_num&quot;</span>, <span class="number">12</span>, <span class="number">4</span>]&#125;,]&#125;,</span><br><span class="line">    <span class="string">&quot;card_expiry&quot;</span>: &#123;<span class="string">&quot;$add&quot;</span>: [<span class="string">&quot;$card_expiry&quot;</span>, &#123;<span class="string">&quot;$floor&quot;</span>: &#123;<span class="string">&quot;$multiply&quot;</span>: [&#123;<span class="string">&quot;$subtract&quot;</span>: [&#123;<span class="string">&quot;$rand&quot;</span>: &#123;&#125;&#125;, <span class="number">0.5</span>]&#125;, <span class="number">2</span>*<span class="number">30</span>*<span class="number">24</span>*<span class="number">60</span>*<span class="number">60</span>*<span class="number">1000</span>]&#125;&#125;,]&#125;,</span><br><span class="line">    <span class="string">&quot;card_sec_code&quot;</span>: &#123;<span class="string">&quot;$concat&quot;</span>: [</span><br><span class="line">                       &#123;<span class="string">&quot;$toString&quot;</span>: &#123;<span class="string">&quot;$floor&quot;</span>: &#123;<span class="string">&quot;$multiply&quot;</span>: [&#123;<span class="string">&quot;$rand&quot;</span>: &#123;&#125;&#125;, <span class="number">10</span>]&#125;&#125;&#125;,</span><br><span class="line">                       &#123;<span class="string">&quot;$toString&quot;</span>: &#123;<span class="string">&quot;$floor&quot;</span>: &#123;<span class="string">&quot;$multiply&quot;</span>: [&#123;<span class="string">&quot;$rand&quot;</span>: &#123;&#125;&#125;, <span class="number">10</span>]&#125;&#125;&#125;,</span><br><span class="line">                       &#123;<span class="string">&quot;$toString&quot;</span>: &#123;<span class="string">&quot;$floor&quot;</span>: &#123;<span class="string">&quot;$multiply&quot;</span>: [&#123;<span class="string">&quot;$rand&quot;</span>: &#123;&#125;&#125;, <span class="number">10</span>]&#125;&#125;&#125;,</span><br><span class="line">                     ]&#125;,</span><br><span class="line">    <span class="string">&quot;transaction_amount&quot;</span>: &#123;<span class="string">&quot;$add&quot;</span>: [<span class="string">&quot;$transaction_amount&quot;</span>, &#123;<span class="string">&quot;$multiply&quot;</span>: [&#123;<span class="string">&quot;$subtract&quot;</span>: [&#123;<span class="string">&quot;$rand&quot;</span>: &#123;&#125;&#125;, <span class="number">0.5</span>]&#125;, <span class="number">0.2</span>, <span class="string">&quot;$transaction_amount&quot;</span>]&#125;,]&#125;,</span><br><span class="line">    <span class="string">&quot;reported&quot;</span>: &#123;<span class="string">&quot;$cond&quot;</span>: &#123;</span><br><span class="line">                   <span class="string">&quot;if&quot;</span>:   &#123;<span class="string">&quot;$lte&quot;</span>: [&#123;<span class="string">&quot;$rand&quot;</span>: &#123;&#125;&#125;, <span class="number">0.8</span>]&#125;,</span><br><span class="line">                   <span class="string">&quot;then&quot;</span>: <span class="string">&quot;$reported&quot;</span>,</span><br><span class="line">                   <span class="string">&quot;else&quot;</span>: &#123;<span class="string">&quot;$not&quot;</span>: [<span class="string">&quot;$reported&quot;</span>]&#125;,</span><br><span class="line">                &#125;&#125;,      </span><br><span class="line">    <span class="string">&quot;customer_info&quot;</span>: &#123;<span class="string">&quot;$cond&quot;</span>: &#123;</span><br><span class="line">                        <span class="string">&quot;if&quot;</span>:   &#123;<span class="string">&quot;$eq&quot;</span>: [<span class="string">&quot;$customer_info.category&quot;</span>, <span class="string">&quot;RESTRICTED&quot;</span>]&#125;, </span><br><span class="line">                        <span class="string">&quot;then&quot;</span>: <span class="string">&quot;$$REMOVE&quot;</span>,     </span><br><span class="line">                        <span class="string">&quot;else&quot;</span>: <span class="string">&quot;$customer_info&quot;</span>,</span><br><span class="line">                     &#125;&#125;,                                         </span><br><span class="line">    <span class="string">&quot;_id&quot;</span>: <span class="string">&quot;$$REMOVE&quot;</span>,                </span><br><span class="line">  &#125;&#125;,</span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;card_name&quot;</span>: &#123;<span class="string">&quot;$concat&quot;</span>: [<span class="string">&quot;Mx. Xxx &quot;</span>, &#123;<span class="string">&quot;$ifNull&quot;</span>: [<span class="string">&quot;$card_name.match&quot;</span>, <span class="string">&quot;Anonymous&quot;</span>]&#125;]&#125;,&#125;&#125;,</span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行聚合操作后应该返回与源文档对应的2个文档，但它们的许多字段都被编辑和混淆了，并且标记为<code>RESTRICTED</code>的<code>customer_info</code>字段被省略了，如下所示。<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-4隐藏敏感字段.png" alt="隐藏敏感字段"></p>
<ul>
<li><p><strong>Targeted Redaction.</strong> The pipeline uses a <code>$cond</code> operator to return the <code>$$REMOVE</code> marker variable if the <code>category</code> field is equal to <code>RESTRICTED</code>. This informs the aggregation engine to exclude the whole <code>customer_info</code> sub-document from the stage’s output for the record. Alternatively, the pipeline could have used a <code>$redact</code> stage to achieve the same. However, <code>$redact</code> typically has to perform more processing work due to needing to check every field in the document. Hence, if a pipeline is only to redact out one specific sub-document, use the approach outlined in this example.</p>
</li>
<li><p><strong>Regular Expression.</strong> For masking the <code>card_name</code> field, a regular expression operator is used to extract the last word of the field’s original value. <code>$regexFind</code> returns metadata into the stage’s output records, indicating if the match succeeded and what the matched value is. Therefore, an additional <code>$set</code> stage is required later in the pipeline to extract the actual matched word from this metadata and prefix it with some hard-coded text.</p>
</li>
<li><p><strong>Meaningful Insight.</strong> Even though the pipeline is irreversibly obfuscating fields, it doesn’t mean that the masked data is useless for performing analytics to gain insight. The pipeline masks some fields by fluctuating the original values by a small but limited random percentage (e.g. <code>card_expiry</code>, <code>transaction_amount</code>), rather than replacing them with completely random values (e.g. <code>card_sec_code</code>). In such cases, if the input data set is sufficiently large, then minor variances will be equalled out. For the fields that are only varied slightly, users can derive similar trends and patterns from analysing the masked data as they would the original data.</p>
</li>
<li><p><strong>Further Reading.</strong> This example is based on the output of two blog posts: 1) <a target="_blank" rel="noopener" href="https://pauldone.blogspot.com/2021/02/mongdb-data-masking.html">MongoDB Irreversible Data Masking</a>, and 2) <a target="_blank" rel="noopener" href="https://pauldone.blogspot.com/2021/02/mongdb-reversible-data-masking.html">MongoDB Reversible Data Masking</a>.</p>
</li>
</ul>
<h2 id="6-时间序列"><a href="#6-时间序列" class="headerlink" title="6.时间序列"></a>6.时间序列</h2><h3 id="1-IOT电力消耗"><a href="#1-IOT电力消耗" class="headerlink" title="(1)IOT电力消耗"></a>(1)IOT电力消耗</h3><p>由于使用<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/timeseries-collections/">时间序列集合</a>、<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/">$setWindowFields</a>阶段和<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/integral/">$integral</a>运算符，要求MongoDB版本最低为 5.0。</p>
<blockquote>
<p>您正在监控工业园区两栋建筑中运行的各种空调机组。每 30 分钟，每个机组中的一个设备将该机组当前的功耗读数发送回基地，中央数据库将保留该读数。</p>
<p>您想分析此数据，以查看每个空调机组在过去一小时内针对收到的每个读数所消耗的能量（以千瓦时 (kWh) 为单位）。此外，您还想计算每个建筑物中所有空调机组每小时消耗的总能量。</p>
<p>首先创建一个<code>device_readings</code>集合，包含了两栋不同建筑物一天内的3个小时之间的设备读数，使用「时间序列集合」进行优化处理：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">db.<span class="title function_">createCollection</span>(<span class="string">&quot;device_readings&quot;</span>,</span><br><span class="line">    &#123;<span class="string">&quot;timeseries&quot;</span>: &#123;<span class="string">&quot;timeField&quot;</span>: <span class="string">&quot;timestamp&quot;</span>, <span class="string">&quot;metaField&quot;</span>: <span class="string">&quot;deviceID&quot;</span>, <span class="string">&quot;granularity&quot;</span>: <span class="string">&quot;minutes&quot;</span>&#125;&#125;</span><br><span class="line">  );</span><br></pre></td></tr></table></figure><br>注意，这个命令可以被注释掉，完整的例子仍然有效。<code>device_readings</code>集合的18条数据如下，分别为<code>ABC</code>和<code>XYZ</code>两栋建筑物的<code>111</code>、<code>222</code>和<code>666</code>三台设备在<code>11:29am</code>、<code>11:59am</code>、<code>12:29pm</code>、<code>12:59pm</code>、<code>13:29pm</code>、<code>13:59pm</code>的三个小时6个时段的电表读数。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-111&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T11:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">8</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-222&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T11:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">7</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-XYZ&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-666&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T11:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">10</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-222&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T11:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">9</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-111&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T11:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">8</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-XYZ&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-666&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T11:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">11</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-222&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T12:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">9</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-111&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T12:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">9</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-XYZ&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-666&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T12:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">10</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-222&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T12:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">8</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-111&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T12:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">8</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-XYZ&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-666&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T12:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">11</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-222&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T13:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">9</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-111&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T13:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">9</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-XYZ&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-666&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T13:29:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">10</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-222&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T13:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">8</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-ABC&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-111&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T13:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">8</span>,     </span><br><span class="line">&#125;, &#123;</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;Building-XYZ&quot;</span>, <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;UltraAirCon-666&quot;</span>,    </span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="title class_">ISODate</span>(<span class="string">&quot;2021-07-03T13:59:59Z&quot;</span>), <span class="string">&quot;powerKilowatts&quot;</span>: <span class="number">11</span>,     </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>为了计算空调机组在过去一小时内针对收到的每个读数所消耗的能量，定义一个「原始读数」的聚合管道如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipelineRawReadings = [</span><br><span class="line">  &#123;<span class="string">&quot;$setWindowFields&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;partitionBy&quot;</span>: <span class="string">&quot;$deviceID&quot;</span>,</span><br><span class="line">    <span class="string">&quot;sortBy&quot;</span>: &#123;<span class="string">&quot;timestamp&quot;</span>: <span class="number">1</span>&#125;,    </span><br><span class="line">    <span class="string">&quot;output&quot;</span>: &#123;<span class="string">&quot;consumedKilowattHours&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;$integral&quot;</span>: &#123;<span class="string">&quot;input&quot;</span>: <span class="string">&quot;$powerKilowatts&quot;</span>, <span class="string">&quot;unit&quot;</span>: <span class="string">&quot;hour&quot;</span>,&#125;,</span><br><span class="line">        <span class="string">&quot;window&quot;</span>: &#123;<span class="string">&quot;range&quot;</span>: [-<span class="number">1</span>, <span class="string">&quot;current&quot;</span>],<span class="string">&quot;unit&quot;</span>: <span class="string">&quot;hour&quot;</span>,&#125;,</span><br><span class="line">      &#125;&#125;,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>为了计算每个建筑物中所有空调机组每小时消耗的总能量，定义一个「建筑物概要」的聚合管道如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pipelineBuildingsSummary = [</span><br><span class="line">  &#123;<span class="string">&quot;$setWindowFields&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;partitionBy&quot;</span>: <span class="string">&quot;$deviceID&quot;</span>,</span><br><span class="line">    <span class="string">&quot;sortBy&quot;</span>: &#123;<span class="string">&quot;timestamp&quot;</span>: <span class="number">1</span>&#125;,    </span><br><span class="line">    <span class="string">&quot;output&quot;</span>: &#123;<span class="string">&quot;consumedKilowattHours&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;$integral&quot;</span>: &#123;<span class="string">&quot;input&quot;</span>: <span class="string">&quot;$powerKilowatts&quot;</span>, <span class="string">&quot;unit&quot;</span>: <span class="string">&quot;hour&quot;</span>,&#125;,</span><br><span class="line">        <span class="string">&quot;window&quot;</span>: &#123;<span class="string">&quot;range&quot;</span>: [-<span class="number">1</span>, <span class="string">&quot;current&quot;</span>], <span class="string">&quot;unit&quot;</span>: <span class="string">&quot;hour&quot;</span>,&#125;,</span><br><span class="line">      &#125;&#125;,</span><br><span class="line">  &#125;&#125;,</span><br><span class="line">  &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;deviceID&quot;</span>: <span class="number">1</span>, <span class="string">&quot;timestamp&quot;</span>: <span class="number">1</span>&#125;&#125;,    </span><br><span class="line">  &#123;<span class="string">&quot;$group&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;_id&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;deviceID&quot;</span>: <span class="string">&quot;$deviceID&quot;</span>,</span><br><span class="line">      <span class="string">&quot;date&quot;</span>: &#123;<span class="string">&quot;$dateTrunc&quot;</span>: &#123;<span class="string">&quot;date&quot;</span>: <span class="string">&quot;$timestamp&quot;</span>,<span class="string">&quot;unit&quot;</span>: <span class="string">&quot;hour&quot;</span>&#125;&#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    <span class="string">&quot;buildingID&quot;</span>: &#123;<span class="string">&quot;$last&quot;</span>: <span class="string">&quot;$buildingID&quot;</span>&#125;,</span><br><span class="line">    <span class="string">&quot;consumedKilowattHours&quot;</span>: &#123;<span class="string">&quot;$last&quot;</span>: <span class="string">&quot;$consumedKilowattHours&quot;</span>&#125;,</span><br><span class="line">  &#125;&#125;,    </span><br><span class="line">  &#123;<span class="string">&quot;$group&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;_id&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;$buildingID&quot;</span>,</span><br><span class="line">      <span class="string">&quot;dayHour&quot;</span>: &#123;<span class="string">&quot;$dateToString&quot;</span>: &#123;<span class="string">&quot;format&quot;</span>: <span class="string">&quot;%Y-%m-%d  %H&quot;</span>, <span class="string">&quot;date&quot;</span>: <span class="string">&quot;$_id.date&quot;</span>&#125;&#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    <span class="string">&quot;consumedKilowattHours&quot;</span>: &#123;<span class="string">&quot;$sum&quot;</span>: <span class="string">&quot;$consumedKilowattHours&quot;</span>&#125;,</span><br><span class="line">  &#125;&#125;,    </span><br><span class="line">  &#123;<span class="string">&quot;$sort&quot;</span>: &#123;<span class="string">&quot;_id.buildingID&quot;</span>: <span class="number">1</span>, <span class="string">&quot;_id.dayHour&quot;</span>: <span class="number">1</span>&#125;&#125;,    </span><br><span class="line">  &#123;<span class="string">&quot;$set&quot;</span>: &#123;<span class="string">&quot;buildingID&quot;</span>: <span class="string">&quot;$_id.buildingID&quot;</span>, <span class="string">&quot;dayHour&quot;</span>: <span class="string">&quot;$_id.dayHour&quot;</span>, <span class="string">&quot;_id&quot;</span>: <span class="string">&quot;$$REMOVE&quot;</span>&#125;&#125;,      </span><br><span class="line">];</span><br></pre></td></tr></table></figure>
<p>执行「原始读数」的聚合管道<code>pipelineRawReadings</code>，来计算空调机组在过去一小时内针对收到的每个读数所消耗的能量，返回的结果如下所示（简洁起见，省略<code>_id</code>字段）：<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-5IOT电力消耗1.png" alt="IOT电力消耗之原始数据聚合"></p>
<p>执行「建筑物概要」的聚合管道<code>pipelineBuildingsSummary</code>，来计算每个建筑物中所有空调机组每小时消耗的总能量，返回的结果如下所示：<br><img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210929-5IOT电力消耗2.png" alt="IOT电力消耗之建筑物概要聚合"></p>
<ul>
<li><p><strong>Integral Trapezoidal Rule.</strong> As <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/aggregation/integral/">documented in the MongoDB Manual</a>, <code>$integral</code> <em>“returns an approximation for the mathematical integral value, which is calculated using the trapezoidal rule”</em>. For non-mathematicians, this explanation may be hard to understand. You may find it easier to comprehend the behaviour of the <code>$integral</code> operator by studying the illustration below and the explanation that follows:</p>
<p>  <img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/MongoDB/20210822梯形法则示例.png" alt="使用梯形法则通过近似积分计算功耗的示例"></p>
<p>  Essentially the <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Trapezoidal_rule">trapezoidal rule</a> determines the area of a region between two points under a graph by matching the region with a trapezoid shape that approximately fits this region and then calculating the area of this trapezoid. You can see a set of points on the illustrated graph with the matched trapezoid shape underneath each pair of points. For this IOT Power Consumption example, the points on the graph represent an air-conditioning unit’s power readings captured every 30 minutes. The Y-axis is the <em>power rate</em> in Kilowatts, and the X-axis is <em>time</em> to indicate when the device captured each reading. Consequently, for this example, the energy consumed by the air-conditioning unit for a given hour’s span is the area of the hour’s specific section under the graph. This section’s area is approximately the area of the two trapezoids shown. Using the <code>$integral</code> operator for the window of time you define in the <code>$setWindowFields</code> stage, you are asking for this approximate area to be calculated, which is the Kilowatt-hours consumed by the air-conditioning unit in one hour.</p>
</li>
<li><p><strong>Window Range Definition.</strong> For every captured document representing a device reading, this example’s pipeline identifies a window of <em>1-hour</em> of previous documents relative to this <em>current</em> document. The pipeline uses this set of documents as the input for the <code>$integral</code> operator. It defines this window range in the setting <code>range: [-1, &quot;current&quot;], unit: &quot;hour&quot;</code>. The pipeline assigns the output of the <code>$integral</code> calculation to a new field called <code>consumedKilowattHours</code>.</p>
</li>
<li><p><strong>One Hour Range Vs Hours Output.</strong> The fact that the <code>$setWindowFields</code> stage in the pipeline defines <code>unit: &quot;hour&quot;</code> in two places may appear redundant at face value. However, this is not the case, and each serves a different purpose. As described in the previous observation, <code>unit: &quot;hour&quot;</code> for the <code>&quot;window&quot;</code> option helps dictate the size of the window of the previous number of documents to analyse. However, <code>unit: &quot;hour&quot;</code> for the <code>$integral</code> operator defines that the output should be in hours (“Kilowatt-hours” in this example), yielding the result <code>consumedKilowattHours: 8.5</code> for one of the processed device readings. However, if the pipeline defined this <code>$integral</code> parameter to be <code>&quot;unit&quot;: &quot;minute&quot;</code> instead, which is perfectly valid, the output value would be <code>510</code> Kilowatt-minutes (i.e. 8.5 x 60 minutes).</p>
</li>
<li><p><strong>Optional Time Series Collection.</strong> This example uses a <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/timeseries-collections/">time series collection</a> to store sequences of device measurements over time efficiently. Employing a time series collection is optional, as shown in the <code>NOTE</code> Javascript comment in the example code. The aggregation pipeline does not need to be changed and achieves the same output if you use a regular collection instead. However, when dealing with large data sets, the aggregation will complete quicker by employing a time series collection.</p>
</li>
<li><p><strong>Index for Partition By &amp; Sort By.</strong> In this example, you define the index <code>&#123;deviceID: 1, timestamp: 1&#125;</code> to optimise the use of the combination of the <code>partitionBy</code> and <code>sortBy</code> parameters in the <code>$setWindowFields</code> stage. This means that the aggregation runtime does not have to perform a slow in-memory sort based on these two fields, and it also avoids the pipeline stage memory limit of 100 MB. It is beneficial to use this index regardless of whether you employ a regular collection or adopt a time series collection.</p>
</li>
</ul>

      
    </div>
    <div class="article-footer">
      <blockquote class="mt-2x">
  <ul class="post-copyright list-unstyled">
    <li class="post-copyright-license">
      <strong>文章作者： </strong><a href="https://hwame.top" style="color:#E541E5;"> 鴻塵</a>
    </li>
    
    <li class="post-copyright-link hidden-xs">
      <strong>本文链接：</strong>
      <a href="https://hwame.top/20210814/mongodb-aggregation-examples.html" title="MongoDB聚合示例" target="_blank" rel="external" style="color:#E541E5;">https://hwame.top/20210814/mongodb-aggregation-examples.html</a>
    </li>
    
    <li class="post-copyright-license">
      <strong>版权声明： </strong> 本博客所有文章除特别声明外，均采用《<a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external" style="color:#E541E5;"><u>CC BY 4.0 CN协议</u></a>》许可协议。转载请注明出处！
    </li>
  </ul>
</blockquote>


<div class="panel panel-default panel-badger">
  <div class="panel-body">
    <figure class="media">
      <div class="media-left">
        <a href="https://hwame.top" target="_blank" class="img-burn thumb-sm visible-lg">
          <img src="https://cdn.jsdelivr.net/gh/hwame/pics@main/avatar.jpg" class="img-rounded w-full" alt="">
        </a>
      </div>
      <div class="media-body">
        <h3 class="media-heading"><a href="https://hwame.top" target="_blank"><span class="text-dark">鴻塵</span><small class="ml-1x">Pythoner, Data Analyst</small></a></h3>
        <div>个人简介：处女座不适合做码农。</div>
      </div>
    </figure>
  </div>
</div>


    </div>
  </article>
  
    
  <section id="comments">
  	
      <div id="vcomments"></div>
    
  </section>


  
</div>

  <nav class="bar bar-footer clearfix" data-stick-bottom>
  <div class="bar-inner">
  
  <ul class="pager pull-left">
    
    <li class="prev">
      <a href="/20210904/make-your-windows-productive.html" title="让你的Windows更具生产力"><i class="icon icon-angle-left" aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
    </li>
    
    
    <li class="next">
      <a href="/20210806/mongodb-aggregation-operation.html" title="MongoDB聚合操作"><span>下一篇&nbsp;&nbsp;</span><i class="icon icon-angle-right" aria-hidden="true"></i></a>
    </li>
    
    
    <li class="toggle-toc">
      <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false" title="文章目录" role="button">
        <span style="color:#C71585">[&nbsp;</span><span style="color:#C71585">文章目录</span>
        <i class="text-collapsed icon icon-anchor"></i>
        <i class="text-in icon icon-close"></i>
        <span style="color:#C71585">]</span>
      </a>
    </li>
    
  </ul>
  
  
  
  <div class="bar-right">
    
    <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter" data-mobile-sites="weibo,wechat,qq,qzone"></div>
    
  </div>
  </div>
</nav>
  


</main>

  <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
	
	
    <ul class="social-links">
    	
        <li><a href="https://github.com/hwame" target="_blank" title="Github" data-toggle=tooltip data-placement=top><i class="icon icon-github"></i></a></li>
        
        <li><a href="https://weibo.com/hwamei" target="_blank" title="Weibo" data-toggle=tooltip data-placement=top><i class="icon icon-weibo"></i></a></li>
        
        <li><a href="https://www.zhihu.com/people/hwame" target="_blank" title="Zhihu" data-toggle=tooltip data-placement=top><i class="icon icon-zhihu"></i></a></li>
        
        <li><a href="https://segmentfault.com/u/hwame" target="_blank" title="Segmentfault" data-toggle=tooltip data-placement=top><i class="icon icon-segmentfault"></i></a></li>
        
        <li><a href="https://gitee.com/hwame" target="_blank" title="Gitee" data-toggle=tooltip data-placement=top><i class="icon icon-gitee"></i></a></li>
        
        <li><a href="/atom.xml" target="_blank" title="Rss" data-toggle=tooltip data-placement=top><i class="icon icon-rss"></i></a></li>
        
    </ul>

    <div class="copyright">
    	
        &copy; 2025 鴻塵
        
        
        <div class="publishby" style="font-family:Courier">
            <span id="busuanzi_container_site_pv">
                <i class="icon icon-eye"></i><span id="busuanzi_value_site_pv" style="font-family:Courier"></span>&nbsp;
                <i class="icon icon-users"></i><span id="busuanzi_value_site_uv" style="font-family:Courier"></span>
            </span>
            <!--
            <br>✯<a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/" style="color:#0FFFAA">鄂ICP备2020019329号</a>
            -->
            <br><i class="icon icon-clock" style="transform:rotate(90deg)"></i><span id="sitetime" style="font-family:Courier"></span>
        </div>
        
        <!--
        <div class="publishby">
        	Theme by <a href="https://github.com/cofess" target="_blank"> cofess </a>base on <a href="https://github.com/cofess/hexo-theme-pure" target="_blank">pure</a>.
        </div>
        -->
    </div>
    
</footer>
<script>
    function siteTime(){
        window.setTimeout("siteTime()", 1000);
        var seconds = 1000;
        var minutes = seconds * 60;
        var hours = minutes * 60;
        var days = hours * 24;
        var years = days * 365;
        var today = new Date();
        var todayYear = today.getFullYear();
        var todayMonth = today.getMonth()+1;
        var todayDate = today.getDate();
        var todayHour = today.getHours();
        var todayMinute = today.getMinutes();
        var todaySecond = today.getSeconds();
        /* Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
        year - 作为date对象的年份，为4位年份值
        month - 0-11之间的整数，做为date对象的月份
        day - 1-31之间的整数，做为date对象的天数
        hours - 0(午夜24点)-23之间的整数，做为date对象的小时数
        minutes - 0-59之间的整数，做为date对象的分钟数
        seconds - 0-59之间的整数，做为date对象的秒数
        microseconds - 0-999之间的整数，做为date对象的毫秒数 */
        var t1 = Date.UTC(2020,05,19,21,20,52); //建站时间
        var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
        var diff = t2-t1;
        var diffYears = Math.floor(diff/years);
        var diffDays = Math.floor((diff/days)-diffYears*365);
        var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
        var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
        var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
        // 将「数字」转「补零字符串」
        var y = diffYears.toString()
        var d = diffDays.toString().padStart(3, '0')
        var h = diffHours.toString().padStart(2, '0')
        var m = diffMinutes.toString().padStart(2, '0')
        var s = diffSeconds.toString().padStart(2, '0')
        document.getElementById("sitetime").innerHTML=y+":"+d+":"+h+":"+m+":"+s;
    }
    siteTime();
</script>
  <script src="//cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
</script>
<div id="go-top"></div>
<style type="text/css">
#go-top {
 width:40px;height:40px;
 background-color:#DDA0DD;
 position:relative;
 border-radius:20px;
 position:fixed;right:20px;bottom:50px;
 cursor:pointer;display:none;
}
#go-top:after {
 content:" ";
 position:absolute;left:14px;top:14px;
 border-top:2px solid #fff;border-right:2px solid #fff;
 width:12px;height:12px;
 transform:rotate(-45deg);
}
#go-top:hover {
 background-color:#8A2BE2;
}
</style>
<script>
$(function () {
  var top=$("#go-top");
  $(window).scroll(function () {
    ($(window).scrollTop() > 300) ? top.show(300) : top.hide(200);
    $("#go-top").click(function () {
      $('body,html').animate({scrollTop:0});
      return false();
    })
  });
});
</script>

<script src="/js/plugin.min.js"></script>


<script src="/js/application.js"></script>


    <script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>

<script src="/js/insight.js"></script>





   




   
    

  <script src='/waline.js'></script>
  <link rel="stylesheet" type="text/css" href="/waline.css" />
  
  <script type="text/javascript">
 
  
Waline.init({
      el: '#vcomments',
      serverURL: 'https://waline.hwame.top',
});
  </script>

     


<!-- 20201211添加判断 -->

   
  <script src="//cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js"></script>
  <script>
  //利用 FancyBox 实现点击图片放大
  $(document).ready(function() {
    $('article img').not('[hidden]').not('.panel-body img').each(function() {
      var $image = $(this);
      var imageCaption = $image.attr('alt');
      var $imageWrapLink = $image.parent('a');
      if ($imageWrapLink.length < 1) {
        var src = this.getAttribute('src');
        var idx = src.lastIndexOf('?');
        if (idx != -1) {
          src = src.substring(0, idx);
        }
        $imageWrapLink = $image.wrap('<a href="' + src + '"></a>').parent('a');
      }
      $imageWrapLink.attr('data-fancybox', 'images');
      if (imageCaption) {
        $imageWrapLink.attr('data-caption', imageCaption);
      }
    });
    $().fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: true,
    });
  });
  </script>







  
  <style>
    .copy-btn {
      display: inline-block;
      padding: 6px 12px;
      font-size: 13px;
      font-weight: 700;
      line-height: 20px;
      color: #333;
      white-space: nowrap;
      vertical-align: middle;
      cursor: pointer;
      background-color: #eee;
      background-image: linear-gradient(#fcfcfc, #eee);
      border: 1px solid #d5d5d5;
      border-radius: 3px;
      user-select: none;
      outline: 0;
    }

    .highlight-wrap .copy-btn {
      transition: opacity .3s ease-in-out;
      opacity: 0;
      padding: 2px 6px;
      position: absolute;
      right: 4px;
      top: 8px;
      z-index: 2;
    }

    .highlight-wrap:hover .copy-btn,
        .highlight-wrap .copy-btn:focus {
      opacity: 1
    }

    .highlight-wrap {
      position: relative;
    }
  </style>
  
  <script>
    addLoadEvent(()=>{
      $('.highlight').each(function (i, e) {
        var $wrap = $('<div>').addClass('highlight-wrap')
        $(e).after($wrap)
        $wrap.append($('<button>').addClass('copy-btn').append('一键复制').on('click', function (e) {
          var code = $(this).parent().find(".code")[0].innerText
          
          var ta = document.createElement('textarea')
          document.body.appendChild(ta)
          ta.style.position = 'absolute'
          ta.style.top = '0px'
          ta.style.left = '0px'
          ta.value = code
          ta.select()
          ta.focus()
          var result = document.execCommand('copy')
          document.body.removeChild(ta)
          
            if(result)$(this).text('复制成功')
            else $(this).text('复制失败')
          
          $(this).blur()
        })).on('mouseleave', function (e) {
          var $b = $(this).find('.copy-btn')
          setTimeout(function () {
            $b.text('一键复制')
          }, 300)
        }).append(e)
      })
    })
  </script>

<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [ ["$","$"], ["\\(","\\)"] ],
            skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
            processEscapes: true
        }
    });
    MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax();
        for (var i = 0; i < all.length; ++i)
            all[i].SourceElement().parentNode.className += ' has-jax';
    });
</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</body>
</html>